Java make file reader account for specific letters - java

Well I'm almost finished with my world editor thanks to this great community, the only thing I need to know is how I can tell my read File code to process specific letters. When I hit enter on my keyboard I will write coordinates of a Vector3f to a text file, this Vector3f is the posistion of my active GameObject. My ProcessText method can read a text file and process the coordinates however he can only read ony type of format:
public void ProcessText()
{
String file_name = "C:/Users/Server/Desktop/textText.txt";
try
{
ProcessCoords file = new ProcessCoords(file_name);
String[] aryLines = file.OpenFile();
int i;
for (i = 0; i < aryLines.length; i++)
{
System.out.println(aryLines[i]);
if(aryLines[i].startsWith("makeGrass:")) {
String Arguments = aryLines[i].substring(aryLines[i].indexOf(":")+1, aryLines[i].length());
String[] ArgArray = Arguments.split(",");
this.makeGrass(Double.parseDouble(ArgArray[0]),
Double.parseDouble(ArgArray[1]),
Double.parseDouble(ArgArray[2]));
}
}
} catch(IOException e) {
System.out.println(e.getMessage());
}
}
In the above example my ProcessText method can only process the coordinates if they are written like this:
makeGrass:x,y,z //for example makeGrass:5,1,9
But when I press enter and write the coordinates from what me my engine gives I'm getting a different format:
makeGrass:(x y z) //for example makeGrass:(3 1 4)
Now what I need to know is how I have to rewrite the code in my ProcessText method so it accounts for the other format that has brackets at the beginning and end and also with spaces to sepearta x from y and y from z instead of commas.
I really don't knwo where else I would find an answer to this question so I'd apreciate any help and explanation as to how this works.
Thanks a lot in advance!

You want to accept as many formats as possible?
Instead of splitting I would try to match, this is safer and doesn't need any pre- or post-processing of the input or the received substrings:
Pattern pattern = Pattern.compile("([0-9]+)"); // outside of method
long[] ArgArray = new long[3];
Matcher matcher = pattern.matcher(Arguments);
int i = 0;
while (matcher.find() && i < 3) {
ArgArray[i++] = Long.parseLong(matcher.group(1));
}
// there was a mistake if i < 3, otherwise you have 3 numbers in ArgArray
If you want to split, you could maybe try this: split("[^0-9]+")
To only match makeGrass:(x y z)
Pattern pattern = Pattern.compile("^makeGrass:\\(([0-9]+) ([0-9]+) ([0-9]+)\\)$");
Like this you can directly match the line and have the 3 numbers in groups 1 - 3 (as String) after calling find once, if (matcher.find()) will decide if it's a valid makeGrass line and if so it can be processed in the if.

if you can guarantee that there will not be any spaces in the makeGrass:x,y,z format and that there will not be any parenthesis in it either then you can use String.replaceAll()... Something like below:
myString = "makeGrass:(3 1 4)"
myString = myString.replaceAll("\(", ""); //replace ( with empty space
myString = myString.replaceAll("\)", ""); //replace ) with empty space
myString = myString.replaceAll(" ", ","); //replace spaces with commas
then you don't need to different methods to handle the two types of input. just format as shown above and pass both inputs to the same method
Going this way you will not need to split on certain chars and then rebuild the string to fit your format

Just split with the regular expression : [\s,]
Splits the String at places where there is either a white space or a ,.
And use this to get rid of any brackets if present :
Arguments = Arguments.replaceAll("\\(", "").replaceAll("\\)", "");
( and ) are part of regex notation. So, they need to be escaped with \ and \ being a Java notation, needs to be escaped with another\. Hence it becomes `"\(". And we have to replace the string and store it back to the String variable. Because Java is pass by value. Both the operations are done in the same line.
The modified code for the method is :
public void ProcessText() {
String file_name = "C:/Users/Server/Desktop/textText.txt";
public void ProcessText()
{
String file_name = "C:/Users/Server/Desktop/textText.txt";
try
{
ProcessCoords file = new ProcessCoords(file_name);
String[] aryLines = file.OpenFile();
int i;
for (i = 0; i < aryLines.length; i++)
{
System.out.println(aryLines[i]);
if(aryLines[i].startsWith("makeGrass:")) {
String Arguments = aryLines[i].substring(aryLines[i].indexOf(":")+1, aryLines[i].length());
Arguments = Arguments.replaceAll("\\(", "").replaceAll("\\)", "");
String[] ArgArray = Arguments.split("[\\s,]");
this.makeGrass(Double.parseDouble(ArgArray[0]),
Double.parseDouble(ArgArray[1]),
Double.parseDouble(ArgArray[2]));
}
}
} catch(IOException e) {
System.out.println(e.getMessage());
}
}

Related

Combine arguments using double quotes

I'm making a TeamSpeak3 ServerQuery bot, command line style. I already have the commands down, but what I can't seem to wrap my head around is the arguments to a command. I use a reset() method to create a List of arguments so combining the string(s) would be easier.
For example, say I change a setting in memory for my bot name
set query name "Kyles Bot"
But the program takes "Kyles and Bot" as two different arguments. I want them as one. How would I go about doing this?
Fields needed for reset():
// Keep String[] and 3 strings null for now, they'll be changed.
private String command, to1, to2;
private String[] to3;
private List<String> args = new ArrayList<>();
The reset() method:
private void reset() {
args.clear();
to1 = line.getText();
command = to1.split(" ")[0];
if (to1.split(" ").length > 1) {
to2 = to1.substring(command.length() + 1, to1.length());
to3 = to2.split(" ");
for (int i = 0; i < to3.length; i++) {
if (to3[i].isEmpty() || to3[i].startsWith(" ")) {
System.out.println("Argument null, command cancelled. [" + to3[i] + "]");
break;
} else {
args.add(to3[i]);
}
}
//EDIT2: Removed useless for loop,
//it was my previous attempt to solve the problem.
} else {
//EDIT: This loop here is used to prevent AIOUB
command = to1;
for (int i = 0; i < 5; i++) {
args.add("NullElement");
}
}
}
The problem is this line:
to3 = to2.split(" ");
it splits the read command on every space, including spaces inside quoted text.
You need to split the command line properly, for example using a Regular Expression:
// matches either a "quoted string" or a single word, both followed by any amount of whitespace
Pattern argumentPattern = Pattern.compile("(\"([^\"]|\\\")*\"|\\S+)\\s*");
// loop over all arguments
Matcher m = argumentPattern.matcher(to2);
for (int start = 0; m.find(start); start = m.end()) {
// get a single argument and remove whitespace around it
String argument = m.group(1).trim();
// handle quoted arguments - remove outer quotes and unescape inner ones
if (argument.startsWith("\""))
argument = argument.substring(1, argument.length() - 1).replace("\\\"", "\"");
// ... your code that uses the argument here
}
Please note that this is not a complete implementation of a command line parser – if you receive arbitrary commands, you should look into libraries that do this parsing for you and can handle all the specifics properly.
PS: please use descriptive variable names instead of to1, to2, to3 etc., for example I used argument instead of to3 in my code.

Parsing comma-separated values enclosed with quotes

I'm trying to parse comma separated values that are enclosed in quotes using only standard Java libraries (I know this must be possible)
As an example file.txt contains a new line for each row of
"Foo","Bar","04042013","04102013","Stuff"
"Foo2","Bar2","04042013","04102013","Stuff2"
However when I parse the file with the code I've written so far:
import java.io.*;
import java.util.Arrays;
public class ReadCSV{
public static void main(String[] arg) throws Exception {
BufferedReader myFile = new BufferedReader(new FileReader("file.txt"));
String myRow = myFile.readLine();
while (myRow != null){
//split by comma separated quote enclosed values
//BUG - first and last values get an extra quote
String[] myArray = myRow.split("\",\""); //the problem
for (String item:myArray) { System.out.print(item + "\t"); }
System.out.println();
myRow = myFile.readLine();
}
myFile.close();
}
}
However the output is
"Foo Bar 04042013 04102013 Stuff"
"Foo2 Bar2 04042013 04102013 Stuff2"
Instead of
Foo Bar 04042013 04102013 Stuff
Foo2 Bar2 04042013 04102013 Stuff2
I know I went wrong on the Split but I'm not sure how to fix it.
Before doing split, just remove first double quote and last double quote in myRow variable using below line.
myRow = myRow.substring(1, myRow.length() - 1);
(UPDATE) Also check if myRow is not empty. Otherwise above code will cause exception. For example below code checks if myRow is not empty and then only removes double quotes from the string.
if (!myRow.isEmpty()) {
myRow = myRow.substring(1, myRow.length() - 1);
}
i think you will probably have to go for a stateful approach, basically like the code below (another state would be necessary if you want to allow escaping of quotes within a value):
import java.util.ArrayList;
import java.util.List;
public class CSV {
public static void main(String[] args) {
String s = "\"hello, i am\",\"a string\"";
String x = s;
List<String> l = new ArrayList<String>();
int state = 0;
while(x.length()>0) {
if(state == 0) {
if(x.indexOf("\"")>-1) {
x = x.substring(x.indexOf("\"")+1).trim();
state = 1;
} else {
break;
}
} else if(state == 1) {
if(x.indexOf("\"")>-1) {
String found = x.substring(0,x.indexOf("\""));
System.err.println("found: "+found);
l.add(found);
x = x.substring(x.indexOf("\"")+1).trim();
state = 0;
} else {
throw new RuntimeException("bad format");
}
} else if(state == 2) {
if(x.indexOf(",")>-1) {
x = x.substring(x.indexOf(",")+1).trim();
state = 0;
} else {
break;
}
}
}
for(String f : l) {
System.err.println(f);
}
}
}
Instead, you can use replaceAll, which, for me, looks more suitable for this task:
myRow = myRow.replaceAll("\"", "").replaceAll(","," ");
This will replace all the " with nothing (Will remove them), then it'll replace all , with space (You can increase the number of spaces of course).
The problem in above code snippet is that you are splitting the String based on ",".
on your Line start "foo"," and end ","stuff" the starting and ending quotes does not match with "," so there are not splitted.
so this definitely not a bug in java. in your case you need to handle that part yourself.
You have multiple options to do it. some of them can be like below.
1. If you are sure there will be always a starting " and ending " you can remove them from String before hand before splitting.
2. If the starting " and " are optional, you can first check it with startsWith endsWith and then remove if exists before splitting.
You can simply get the String delimitered by the comma and then delete the first and last '"'.
=)
hope thats helpfull
dont have much time :D
String s = "\"Foo\",\"Bar\",\"04042013\",\"04102013\",\"Stuff\"";
String[] bufferArray = new String[10];
String bufferString;
int i = 0;
System.out.println(s);
Scanner scanner = new Scanner(s);
scanner.useDelimiter(",");
while(scanner.hasNext()) {
bufferString = scanner.next();
bufferArray[i] = bufferString.subSequence(1, bufferString.length() - 1).toString();
i++;
}
System.out.println(bufferArray[0]);
System.out.println(bufferArray[1]);
System.out.println(bufferArray[2]);
This solution is less elegant than a String.split() oneliner. The advantage is that we avoid fragile string manipulation, ie. the use of String.substring(). The string must end with ," however.
This version handles spaces between delimiters. Delimiter characters within quotes are ignored as expected, as are escaped quotes (for example \").
String s = "\"F\\\",\\\"oo\" , \"B,ar\",\"04042013\",\"04102013\",\"St,u\\\"ff\"";
Pattern p = Pattern.compile("(.*?)\"\\s*,\\s*\"");
Matcher m = p.matcher(s + ",\""); // String must end with ,"
while (m.find()) {
String result = m.group(1);
System.out.println(result);
}

Java's split method has leading blank records that I can't suppress

I'm parsing an input file that has multiple keywords preceded by a +. The + is my delimiter in a split, with individual tokens being written to an array. The resulting array includes a blank record in the [0] position.
I suspect that split is taking the "nothing" before the first token and populating project[0], then moving on to subsequent tokens which all show up as correct.
Documentaion says that this method has a limit parameter:
If n is zero then the pattern will be applied as many times as
possible, the array can have any length, and trailing empty strings
will be discarded.
and I found this post on SO, but the solution proposed, editing out the leading delimiter (I used a substring(1) to create a temp field) yielded the same blank record for me.
Code and output appers below. Any tips would be appreciated.
import java.util.regex.*;
import java.io.*;
import java.nio.file.*;
import java.lang.*;
//
public class eadd
{
public static void main(String args[])
{
String projStrTemp = "";
String projString = "";
String[] project = new String[10];
int contextSOF = 0;
int projStringSOF = 0;
int projStringEOF = 0;
//
String inputLine = "foo foofoo foo foo #bar.com +foofoofoo +foo1 +foo2 +foo3";
contextSOF = inputLine.indexOf("#");
int tempCalc = (inputLine.indexOf("+")) ;
if (tempCalc == -1) {
proj StrTemp = "+Uncategorized";
} else {
projStringSOF = inputLine.indexOf("+",contextSOF);
projStrTemp = inputLine.trim().substring(projStringSOF).trim();
}
project = projStrTemp.split("\\+");
//
System.out.println(projStrTemp+"\n"+projString);
for(int j=0;j<project.length;j++) {
System.out.println("Project["+j+"] "+project[j]);
}
}
CONSOLE OUTPUT:
+foofoofoo +foo1 +foo2 +foo3
Project[0]
Project[1] foofoofoo
Project[2] foo1
Project[3] foo2
Project[4] foo3
Change:
projStrTemp = inputLine.trim().substring(projStringSOF).trim();
to:
projStrTemp = inputLine.trim().substring(projStringSOF + 1).trim();
If you have a leading delimiter, your array will start with a blank element. It might be worthwhile for you to experiment with split() without all the other baggage.
public static void main(String[] args) {
String s = "an+example";
String[] items = s.split("\\+");
for (int i = 0; i < items.length; i++) {
System.out.println(i + " = " + items[i]);
}
}
With String s = "an+example"; it produces:
0 = an
1 = example
Whereas String s = "+an+example"; produces:
0 =
1 = an
2 = example
One simple solution would be to remove the first + from the string. This way, it won't split before the first keyword:
projStrTemp = inputLine.trim().substring(projStringSOF + 1).trim();
Edit: Personally, I'd go for a more robust solution using regular expressions. This finds all keywords preceded by +. It also requires that + is preceded by either a space or it's at the start of the line so that words like 3+4 aren't matched.
String inputLine = "+foo 3+4 foofoo foo foo #bar.com +foofoofoo +foo1 +foo2 +foo3";
Pattern re = Pattern.compile("(\\s|^)\\+(\\w+)");
Matcher m = re.matcher(inputLine);
while (m.find()) {
System.out.println(m.group(2));
}
+foofoofoo +foo1 +foo2 +foo3
Splits method splits the string around matches of the given + so the array contains in the first element an empty field (with 5 elements). If you want to get the previous data get inputLine instead the processed projStrTemp that substring from the first + included.

String.split() Not Acting on Semicolon or Space Delimiters

This may be a simple question, but I have been Googling for over an hour and haven't found an answer yet.
I'm trying to simply use the String.split() method with a small Android application to split an input string. The input string will be something along the lines of: "Launch ip:192.168.1.101;port:5900". I'm doing this in two iterations to ensure that all of the required parameters are there. I'm first trying to do a split on spaces and semicolons to get the individual tokens sorted out. Next, I'm trying to split on colons in order to strip off the identification tags of each piece of information.
So, for example, I would expect the first round of split to give me the following data from the above example string:
(1) Launch
(2) ip:192.168.1.101
(3) port:5900
Then the second round would give me the following:
(1) 192.168.1.101
(2) 5900
However, the following code that I wrote doesn't give me what's expected:
private String[] splitString(String inputString)
{
String[] parsedString;
String[] orderedString = new String[SOSLauncherConstants.SOCKET_INPUT_STRING_PARSE_VALUE];
parsedString = inputString.trim().split("; ");
Log.i("info", "The parsed data is as follows for the initially parsed string of size " + parsedString.length + ": ");
for (int i = 0; i < parsedString.length; ++i)
{
Log.i("info", parsedString[i]);
}
for (int i = 0; i < parsedString.length; ++i )
{
if (parsedString[i].toLowerCase().contains(SOSLauncherConstants.PARSED_LAUNCH_COMMAND_VALUE))
{
orderedString[SOSLauncherConstants.PARSED_COMMAND_WORD] = parsedString[i];
}
if (parsedString[i].toLowerCase().contains("ip"))
{
orderedString[SOSLauncherConstants.PARSED_IP_VALUE] = parsedString[i].split(":")[1];
}
else if (parsedString[i].toLowerCase().contains("port"))
{
orderedString[SOSLauncherConstants.PARSED_PORT_VALUE] = parsedString[i].split(":")[1];
}
else if (parsedString[i].toLowerCase().contains("username"))
{
orderedString[SOSLauncherConstants.PARSED_USERNAME_VALUE] = parsedString[i].split(":")[1];
}
else if (parsedString[i].toLowerCase().contains("password"))
{
orderedString[SOSLauncherConstants.PARSED_PASSWORD_VALUE] = parsedString[i].split(":")[1];
}
else if (parsedString[i].toLowerCase().contains("color"))
{
orderedString[SOSLauncherConstants.PARSED_COLOR_VALUE] = parsedString[i].split(":")[1];
}
}
Log.i("info", "The parsed data is as follows for the second parsed string of size " + orderedString.length + ": ");
for (int i = 0; i < orderedString.length; ++i)
{
Log.i("info", orderedString[i]);
}
return orderedString;
}
For a result, I'm getting the following:
The parsed data is as follows for the parsed string of size 1:
launch ip:192.168.1.106;port:5900
The parsed data is as follows for the second parsed string of size 6:
launch ip:192.168.1.106;port:5900
192.168.1.106;port
And then, of course, it crashes because the for loop runs into a null string.
Side Note:
The following snippet is from the constants class that defines all of the string indexes --
public static final int SOCKET_INPUT_STRING_PARSE_VALUE = 6;
public static final int PARSED_COMMAND_WORD = 0;
public static final String PARSED_LAUNCH_COMMAND_VALUE = "launch";
public static final int PARSED_IP_VALUE = 1;
public static final int PARSED_PORT_VALUE = 2;
public static final int PARSED_USERNAME_VALUE = 3;
public static final int PARSED_PASSWORD_VALUE = 4;
public static final int PARSED_COLOR_VALUE = 5;
I looked into needing a possible escape (by inserting a \\ before the semicolon) on the semicolon delimiter, and even tried using it, but that didn't work. The odd part is that neither the space nor the semicolon function as a delimiter, yet the colon works on the second time around. Does anybody have any ideas what would cause this?
Thanks for your time!
EDIT: I should also add that I'm receiving the string over a WiFi socket connection. I don't think this should make a difference, but I'd like you to have all of the information that you need.
String.split(String) takes a regex. Use "[; ]". eg:
"foo;bar baz".split("[; ]")
will return an array containing "foo", "bar" and "baz".
If you need groups of spaces to work as a single delimiter, you can use something like:
"foo;bar baz".split("(;| +)")
I believe String.split() tries to split on each of the characters you specify together (or on a regex), not each character individually. That is, split(";.") would not split "a;b.c" at all, but would split "a;.b".
You may have better luck with Guava's Splitter, which is meant to be slightly less unpredictable than java.lang.String.split.
I would write something like
Iterable<String> splits = Splitter.on(CharMatcher.anyOf("; ")).split(string);
but Splitter also provides fluent-style customization like "trim results" or "skip over empty strings."
Is there a reason why you are using String.split(), but not using Regular Expressions? This is a perfect candidate for regex'es, esp if the string format is consistent.
I'm not sure if your format is fixed, and if it is, then the following regex should break it down for you (am sure that someone can come up with an even more elegant regex). If you have several command strings that follow, then you can use a more flexible regex and loop over all the groups:
Pattern p = Pattern.compile("([\w]*)[ ;](([\w]*):([^ ;]*))*");
Matcher m = p.match( <input string>);
if( m.find() )
command = m.group(1);
do{
id = m.group(3);
value = m.group(4);
} while( m.find() );
A great place to test out regex'es online is http://www.regexplanet.com/simple/index.html. It allows you to play with the regex without having to compile and launch you app every time if you just want to get the regex correct.

Remove last set of value from a comma separated string in java

I wan to remove the last set of data from string using java.
For example I have a string like A,B,C, and I want to remove ,C, and want to get the out put value like A,B . How is it possible in java? Please help.
String start = "A,B,C,";
String result = start.subString(0, start.lastIndexOf(',', start.lastIndexOf(',') - 1));
Here is a fairly "robust" reg-exp solution:
Pattern p = Pattern.compile("((\\w,?)+),\\w+,?");
for (String test : new String[] {"A,B,C", "A,B", "A,B,C,",
"ABC,DEF,GHI,JKL"}) {
Matcher m = p.matcher(test);
if (m.matches())
System.out.println(m.group(1));
}
Output:
A,B
A
A,B
ABC,DEF,GHI
Since there may be a trailing comma, something like this (using org.apache.commons.lang.StringUtils):
ArrayList<String> list = new ArrayList(Arrays.asList(myString.split()));
list.remove(list.length-1);
myString = StringUtils.join(list, ",");
You can use String#lastIndexOf to find the index of the second-to-last comma, and then String#substring to extract just the part before it. Since your sample data ends with a ",", you'll need to use the version of String#lastIndexOf that accepts a starting point and have it skip the last character (e.g., feed in the string's length minus 1).
I wasn't going to post actual code on the theory better to teach a man to fish, but as everyone else is:
String data = "A,B,C,";
String shortened = data.substring(0, data.lastIndexOf(',', data.length() - 2));
You can use regex to do this
String start = "A,B,C,";
String result = start.replaceAll(",[^,]*,$", "");
System.out.println(result);
prints
A,B
This simply erases the the 'second last comma followed by data followed by last comma'
If full String.split() is not possible, the how about just scanning the string for comma and stop after reaching 2nd, without including it in final answer?
String start = "A,B";
StringBuilder result = new StringBuilder();
int count = 0;
for(char ch:start.toCharArray()) {
if(ch == ',') {
count++;
if(count==2) {
break;
}
}
result.append(ch);
}
System.out.println("Result = "+result.toString());
Simple trick, but should be efficient.
In case you want last set of data removed, irrespective of how much you want to read, then
start.substring(0, start.lastIndexOf(',', start.lastIndexOf(',')-1))
Another way to do this is using a StringTokenizer:
String input = "A,B,C,";
StringTokenizer tokenizer = new StringTokenizer(input, ",");
String output = new String();
int tokenCount = tokenizer.countTokens();
for (int i = 0; i < tokenCount - 1; i++) {
output += tokenizer.nextToken();
if (i < tokenCount - 1) {
output += ",";
}
}
public string RemoveLastSepratorFromString(string input)
{
string result = input;
if (result.Length > 1)
{
result = input.Remove(input.Length - 1, 1);
}
return result;
}
// use from above method
string test = "1,2,3,"
string strResult = RemoveLastSepratorFromString(test);
//output --> 1,2,3

Categories