Java parsing alternative to current solution - java

I have a text file to parse, that requires different logic depending on certain conditions. Below, is my current solution that works. However, I find it very clunky, and have been looking into other solutions such as StringTokenizer or Pattern class and am wondering I may be able to implement this more elegantly using them.
Do let me know if I should move this to the Code Review forum--I have not initially put it there, as I am unable to implement the other mentioned solutions.
File file = fileChooser.getSelectedFile();
java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.FileReader(file));
memoryMap = new HashMap<Integer, Integer>();
registerMap = new HashMap<Integer, Integer>();
String line = reader.readLine();
while (line != null) {
if (line.contains("#")) {
System.out.println(line);
line = reader.readLine();
}
if (!Character.isDigit(line.charAt(0))) {
System.out.println(line);
String[] setFirstSplit = line.split(":");
if (setFirstSplit[0].equals("M")) {
boolean isFirst = true;
for (String setFirstSegment : setFirstSplit) {
if (!isFirst) {
String[] setSecondSplit = setFirstSegment.split(",");
for (String setSecondSegment : setSecondSplit) {
String[] setThirdSplit = setSecondSegment.split("=");
for (String setThirdSegment : setThirdSplit) {
System.out.println(setThirdSegment);
memoryMap.put(Integer.parseInt(setThirdSplit[0]), Integer.parseInt(setThirdSplit[1]));
System.out.println("Memory Set Result: " + memoryMap);
}
}
} else {
isFirst = false;
}
}
}
if (setFirstSplit[0].equals("R")) {
boolean isFirst = true;
for (String setFirstSegment : setFirstSplit) {
if (!isFirst) {
String[] setSecondSplit = setFirstSegment.split(",");
for (String setSecondSegment : setSecondSplit) {
String[] setThirdSplit = setSecondSegment.split("=");
for (String setThirdSegment : setThirdSplit) {
System.out.println(setThirdSegment);
registerMap.put(Integer.parseInt(setThirdSplit[0]), Integer.parseInt(setThirdSplit[1]));
System.out.println("Register Set Result: " + registerMap);
}
}
} else {
isFirst = false;
}
}
}
line = reader.readLine();
} else {
System.out.println(line);
String[] actionFirstSplit = line.split(" ");
if (actionFirstSplit[1].equals("LOAD")) {
String[] actionSecondSplit = actionFirstSplit[2].split(",");
LoadStep action = new LoadStep();
action.executeStep(Integer.parseInt(actionSecondSplit[0]), Integer.parseInt(actionSecondSplit[1]));
System.out.println("Memory Action Result: " + memoryMap);
System.out.println("Register Action Result: " + registerMap);
}
else {
System.out.println(line);
}
line = reader.readLine();
}
}
reader.close();
The text file looks like this:
# sets the memory address 0 to store the value 1. M stands for memory.
M:0=1,1=11
# All programs starts with an initial setup of values in memory such as the example shown above
0 LOAD 1,3
1 LOAD 0,2
2 ADD 1,2
3 ADD 0,1
4 LSS 1,3,2
5 STOR 62,1
6 STOP

Write it top-down.
String line = reader.readLine();
while (line != null) {
if (parsedComment(line)) {
} else if (parsedMemory(line)) {
} else if (parsedInstruction(line)) {
} else {
error(...);
}
line = reader.readLine();
}
Parse functions may use fields to pass results, like those maps, or have extra parameters.
(If you have multi-line syntax, the reader might be better placed in a field, and disappear as parameter. You can then read a line ahead in the field, and check on that.)

You could use a parser generator like ANTLR http://www.antlr.org/

Related

How can I scope three different conditions using the same loop in Java?

I would like to count countX and countX using the same loop instead of creating three different loops. Is there any easy way approaching that?
public class Absence {
private static File file = new File("/Users/naplo.txt");
private static File file_out = new File("/Users/naplo_out.txt");
private static BufferedReader br = null;
private static BufferedWriter bw = null;
public static void main(String[] args) throws IOException {
int countSign = 0;
int countX = 0;
int countI = 0;
String sign = "#";
String absenceX = "X";
String absenceI = "I";
try {
br = new BufferedReader(new FileReader(file));
bw = new BufferedWriter(new FileWriter(file_out));
String st;
while ((st = br.readLine()) != null) {
for (String element : st.split(" ")) {
if (element.matches(sign)) {
countSign++;
continue;
}
if (element.matches(absenceX)) {
countX++;
continue;
}
if (element.matches(absenceI)) {
countI++;
}
}
}
System.out.println("2. exerc.: There are " + countSign + " rows int the file with that sign.");
System.out.println("3. exerc.: There are " + countX + " with sick note, and " + countI + " without sick note!");
} catch (FileNotFoundException ex) {
Logger.getLogger(Absence.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
text file example:
# 03 26
Jujuba Ibolya IXXXXXX
Maracuja Kolos XXXXXXX
I think you meant using less than 3 if statements. You can actually so it with no ifs.
In your for loop write this:
Countsign += (element.matches(sign)) ? 1 : 0;
CountX += (element.matches(absenceX)) ? 1 : 0;
CountI += (element.matches(absenceI)) ? 1 : 0;
Both answers check if the word (element) matches all regular expressions while this can (and should, if you ask me) be avoided since a word can match only one regex. I am referring to the continue part your original code has, which is good since you do not have to do any further checks.
So, I am leaving here one way to do it with Java 8 Streams in "one liner".
But let's assume the following regular expressions:
String absenceX = "X*";
String absenceI = "I.*";
and one more (for the sake of the example):
String onlyNumbers = "[0-9]*";
In order to have some matches on them.
The text is as you gave it.
public class Test {
public static void main(String[] args) throws IOException {
File desktop = new File(System.getProperty("user.home"), "Desktop");
File txtFile = new File(desktop, "test.txt");
String sign = "#";
String absenceX = "X*";
String absenceI = "I.*";
String onlyNumbers = "[0-9]*";
List<String> regexes = Arrays.asList(sign, absenceX, absenceI, onlyNumbers);
List<String> lines = Files.readAllLines(txtFile.toPath());
//#formatter:off
Map<String, Long> result = lines.stream()
.flatMap(line-> Stream.of(line.split(" "))) //map these lines to words
.map(word -> regexes.stream().filter(word::matches).findFirst()) //find the first regex this word matches
.filter(Optional::isPresent) //If it matches no regex, it will be ignored
.collect(Collectors.groupingBy(Optional::get, Collectors.counting())); //collect
System.out.println(result);
}
}
The result:
{X*=1, #=1, I.=2, [0-9]=2}
X*=1 came from word: XXXXXXX
#=1 came from word: #
I.*=2 came from words: IXXXXXX and Ibolya
[0-9]*=2 came from words: 03 and 06
Ignore the fact I load all lines in memory.
So I made it with the following lines to work. It escaped my attention that every character need to be separated from each other. Your ternary operation suggestion also nice so I will use it.
String myString;
while ((myString = br.readLine()) != null) {
String newString = myString.replaceAll("", " ").trim();
for (String element : newString.split(" ")) {
countSign += (element.matches(sign)) ? 1 : 0;
countX += (element.matches(absenceX)) ? 1 : 0;
countI += (element.matches(absenceI)) ? 1 : 0;

Handle Empty lines in Java

I am facing a problem in the following code. I am trying to run the program and it terminates when it hits empty space in my input. How else I should approach this.
try {
BufferedReader sc = new BufferedReader(new FileReader(text.txt);
ArrayList<String> name = new ArrayList<>();
ArrayList<String> id = new ArrayList<>();
ArrayList<String> place = new ArrayList<>();
ArrayList<String> details = new ArrayList<>();
String line = null;
while ((line = sc.readLine()) !=null) {
if (!line.trim().equals("")) {
System.out.println(line);
if (line.toLowerCase().contains("name")) {
name.add(line.split("=")[1].trim());
}
if (line.toLowerCase().contains("id")) {
id.add(line.split("=")[1].trim());
}
if (line.toLowerCase().contains("location")) {
place.add(line.split("=")[1].trim());
}
if (line.toLowerCase().contains("details")) {
details.add(line.split("=")[1].trim());
}
}
}
PrintWriter pr = new PrintWriter(new File(text.csv));
pr.println("Name;Id;;Location;Details");
for (int i = 0; i < name.size(); i++) {
pr.println(name.get(i) + ";" + id.get(i) + ";" + place.get(i) + ";" + details.get(i));
}
pr.close();
sc.close();
} catch (Exception e) {
e.printStackTrace();
} }
My Input looks like
name = abc
id = 123
place = xyz
details = hsdyhuslkjaldhaadj
name = ert
id = 7872
place =
details = shahkjdhksdhsala
name = sfd
id = 4343
place = ksjks
Details = kljhaljs
when im trying to execute then above text my program terminates at place = "null" because of no value there.I need the output as an empty space created in place ="null" and print the rest as follows in a .csv file
If you process the location, line.split("=")[1] could result in an ArrayIndexOutOfBoundException and line.split("=")[1].trim() could result in a NullPointerException.
You can avoid this by testing your parsed result.
Instead of place.add(line.split("=")[1].trim());, do place.add(parseContentDefaultEmpty(line));, with:
private String parseContentDefaultEmpty(final String line) {
final String[] result = line.split("=");
if(result.length <= 1) {
return "";
}
final String content = line.split("=")[1];
return content != null ? content.trim() : "";
}
First there is a issue,your input file contains key as "place" but your are trying for word "location"
if (line.toLowerCase().contains("location")) { //this must be changed to place
place.add(line.split("=")[1].trim());
}
Modified the code snippet as below.check it
while ((line = sc.readLine()) != null) {
if (!line.trim().equals("")) {
System.out.println(line);
if (line.toLowerCase().contains("name")) {
name.add(line.split("=")[1].trim());
}
if (line.toLowerCase().contains("id")) {
id.add(line.split("=")[1].trim());
}
if (line.toLowerCase().contains("place")) {
// change done here to add space if no value
place.add(line.split("=").length > 1 ? line.split("=")[1]
.trim() : " ");
}
if (line.toLowerCase().contains("details")) {
details.add(line.split("=")[1].trim());
}
}
}
Setting question to line doesn't appear to change what line is read later (if you're wanting the line to advance before it hits the while loop).

Translate words in a string using BufferedReader (Java)

I've been working on this for a few days now and I just can't make any headway. I've tried using Scanner and BufferedReader and had no luck.
Basically, I have a working method (shortenWord) that takes a String and shortens it according to a text file formatted like this:
hello,lo
any,ne
anyone,ne1
thanks,thx
It also accounts for punctuation so 'hello?' becomes 'lo?' etc.
I need to be able to read in a String and translate each word individually, so "hello? any anyone thanks!" will become "lo? ne ne1 thx!", basically using the method I already have on each word in the String. The code I have will translate the first word but then does nothing to the rest. I think it's something to do with how my BufferedReader is working.
import java.io.*;
public class Shortener {
private FileReader in ;
/*
* Default constructor that will load a default abbreviations text file.
*/
public Shortener() {
try {
in = new FileReader( "abbreviations.txt" );
}
catch ( Exception e ) {
System.out.println( e );
}
}
public String shortenWord( String inWord ) {
String punc = new String(",?.!;") ;
char finalchar = inWord.charAt(inWord.length()-1) ;
String outWord = new String() ;
BufferedReader abrv = new BufferedReader(in) ;
// ends in punctuation
if (punc.indexOf(finalchar) != -1 ) {
String sub = inWord.substring(0, inWord.length()-1) ;
outWord = sub + finalchar ;
try {
String line;
while ( (line = abrv.readLine()) != null ) {
String[] lineArray = line.split(",") ;
if ( line.contains(sub) ) {
outWord = lineArray[1] + finalchar ;
}
}
}
catch (IOException e) {
System.out.println(e) ;
}
}
// no punctuation
else {
outWord = inWord ;
try {
String line;
while( (line = abrv.readLine()) != null) {
String[] lineArray = line.split(",") ;
if ( line.contains(inWord) ) {
outWord = lineArray[1] ;
}
}
}
catch (IOException ioe) {
System.out.println(ioe) ;
}
}
return outWord;
}
public void shortenMessage( String inMessage ) {
String[] messageArray = inMessage.split("\\s+") ;
for (String word : messageArray) {
System.out.println(shortenWord(word));
}
}
}
Any help, or even a nudge in the right direction would be so much appreciated.
Edit: I've tried closing the BufferedReader at the end of the shortenWord method and it just results in me getting an error on every word in the String after the first one saying that the BufferedReader is closed.
So I took at look at this. First of all, if you have the option to change the format of your textfile I would change it to something like this (or XML):
key1=value1
key2=value2
By doing this you could later use java's Properties.load(Reader). This would remove the need for any manual parsing of the file.'
If by any change you don't have the option to change the format then you'll have to parse it yourself. Something like the code below would do that, and put the results into a Map called shortningRules which could then be used later.
private void parseInput(FileReader reader) {
try (BufferedReader br = new BufferedReader(reader)) {
String line;
while ((line = br.readLine()) != null) {
String[] lineComponents = line.split(",");
this.shortningRules.put(lineComponents[0], lineComponents[1]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
When it comes to actually shortening a message I would probably opt for a regex approach, e.g \\bKEY\\b where key is word you want shortened. \\b is a anchor in regex and symbolizes a word boundery which means it will not match spaces or punctuation.
The whole code for doing the shortening would then become something like this:
public void shortenMessage(String message) {
for (Entry<String, String> entry : shortningRules.entrySet()) {
message = message.replaceAll("\\b" + entry.getKey() + "\\b", entry.getValue());
}
System.out.println(message); //This should probably be a return statement instead of a sysout.
}
Putting it all together will give you something this, here I've added a main for testing purposes.
I think you can have a simpler solution using a HashMap. Read all the abbreviations into the map when the Shortener object is created, and just reference it once you have a word. The word will be the key and the abbreviation the value. Like this:
public class Shortener {
private FileReader in;
//the map
private HashMap<String, String> abbreviations;
/*
* Default constructor that will load a default abbreviations text file.
*/
public Shortener() {
//initialize the map
this.abbreviations = new HashMap<>();
try {
in = new FileReader("abbreviations.txt" );
BufferedReader abrv = new BufferedReader(in) ;
String line;
while ((line = abrv.readLine()) != null) {
String [] abv = line.split(",");
//If there is not two items in the file, the file is malformed
if (abv.length != 2) {
throw new IllegalArgumentException("Malformed abbreviation file");
}
//populate the map with the word as key and abbreviation as value
abbreviations.put(abv[0], abv[1]);
}
}
catch ( Exception e ) {
System.out.println( e );
}
}
public String shortenWord( String inWord ) {
String punc = new String(",?.!;") ;
char finalchar = inWord.charAt(inWord.length()-1) ;
// ends in punctuation
if (punc.indexOf(finalchar) != -1) {
String sub = inWord.substring(0, inWord.length() - 1);
//Reference map
String abv = abbreviations.get(sub);
if (abv == null)
return inWord;
return new StringBuilder(abv).append(finalchar).toString();
}
// no punctuation
else {
//Reference map
String abv = abbreviations.get(inWord);
if (abv == null)
return inWord;
return abv;
}
}
public void shortenMessage( String inMessage ) {
String[] messageArray = inMessage.split("\\s+") ;
for (String word : messageArray) {
System.out.println(shortenWord(word));
}
}
public static void main (String [] args) {
Shortener s = new Shortener();
s.shortenMessage("hello? any anyone thanks!");
}
}
Output:
lo?
ne
ne1
thx!
Edit:
From atommans answer, you can basically remove the shortenWord method, by modifying the shortenMessage method like this:
public void shortenMessage(String inMessage) {
for (Entry<String, String> entry:this.abbreviations.entrySet())
inMessage = inMessage.replaceAll(entry.getKey(), entry.getValue());
System.out.println(inMessage);
}

JAVA - CSV file formatting (2 types of lines) – I don’t know how to split the data

i have a CSV file with 2 types of lines:
CSV file formatting
line type 1: "user1=",data1,data2,
line type 2: "user2=",data3,data4,
data5
data6
line type 1: "user3="data6,data7
I am trying to insert each user (with all his data), to Array using this code:
br = new BufferedReader(new FileReader(csvFile));
while ((line = br.readLine()) != null)
{
String[] users = line.split(",");
Type line 1 was split fine.
The problem is to split type line 2, and i don't know how to do it.
expected result
users [user1, data1, data2]
users [user2, data3, data4, data5, data6]
users [user3, data7, data8]
source (http://www.mkyong.com/java/how-to-read-and-parse-csv-file-in-java/)
There is no problem with split function in your code. Use trim() to remove extra space and use substring() to detect first element(i.e. "user1=" or "user2=" etc) an modify it to user1/user2 . I used same example as you provided.
br = new BufferedReader(new FileReader(csvFile));
while ((line = br.readLine()) != null) {
if (line.startsWith("\"user")) {
if (!firstElement)
System.out.print("]\n");
System.out.print("users [");
firstElement = true;
}
// use comma as separato
String[] country = line.split(cvsSplitBy);
for (int i = 0; i < country.length; i++) {
if (country[i].trim().length() != 0) {
if (country[i].endsWith("=\"")) {
country[i] = country[i].substring(0,
country[i].length() - 2);
}
if (firstElement) {
System.out.print(country[i].substring(1));
firstElement = false;
}
else {
System.out.print(", " + country[i]);
}
}
}
}
System.out.print("]\n");

Find word in text file and count specific words

now I'll try to explain what I need to do.
I have file.txt file, It looks like:
John //first line - name
One
Three
Four
Peter //first line - name
Two
Three
Elisa //first line - name
One
Three
Albert //first line - name
One
Three
Four
Nicole //first line - name
Two
Four
So I have program's code:
public class Testing {
public static void main(String args[]) throws Exception {
Scanner input = new Scanner(System.in);
System.out.println("Select word from list:");
System.out.println();
try {
FileReader fr = new FileReader("src/lt/kvk/i3_2/test/List.txt"); // this is list of words, everything all right here
BufferedReader br = new BufferedReader(fr);
String s;
while((s = br.readLine()) != null) {
System.out.println(s);
}
fr.close();
String stilius = input.nextLine(); // eneter word which I want to count in File.txt
BufferedReader bf = new BufferedReader(new FileReader("src/lt/kvk/i3_2/test/File.txt")); // from this file I need to count word which I entered before
int counter = 0;
String line;
System.out.println("Looking for information");
while (( line = bf.readLine()) != null){
int indexfound = line.indexOf(stilius);
if (indexfound > -1) {
counter++;
}
}
if (counter > 0) {
System.out.println("Word are repeated "+ counter + "times");}
else {
System.out.println("Error...");
}
bf.close();
}
catch (IOException e) {
System.out.println("Error:" + e.toString());
}
}
}
This program counting specific word (entered by keyboard) in file.txt.
I need to make this program: for ex.: if I enter word: One It must show:
Word One repeated 3 times by John, Elisa, Albert
All what I need to elect by who this word repeated. But I don't know really how to make It, maybe LinkedList or I dont know, someone could help me?
Thank you very much.
You can have a List<String> that holds where your word was repeated, and add elements to it on the fly.
To add elements to it you can use an extra variable lastName (of type String), and in your while loop, do something like:
if (line.trim().length() == 0) lastName = null;
else if (lastName == null) lastName = line;
The first line if for "resetting" the lastName variable, after you changes the name of the name (assuming there is an empty line after all the words of each name)
The second line is setting it back to the new name after an empty line.
And in addition when you increase counter do:
myList.add(lastName)
So the loop will be something like that:
List<String> resultList = new ArrayList<String>();
String lastName = null;
while (( line = bf.readLine()) != null){
if (line.trim().length() == 0) lastName = null;
else if (lastName == null) lastName = line;
int indexfound = line.indexOf(stilius);
if (indexfound > -1) {
counter++;
resultList.add(lastName);
}
//do something with resultList, which contains the names
}
I'd suggest you to use Map<String, Set<String>> where key is a word and value is the list of people names that "typed" this word.
So, here is how you can define your collection:
Map<String, Set<String>> word2people = HashMap>();`
Here is how you can add words there:
String name = ...
String word = ...
Set<String> people = word2people.get(word);
if (people == null) {
people = new HashSet<String>();
word2people.put(people);
}
people.add(name);
And here is how you can retrieve the value:
word2people.get(word)

Categories