How to design String decollator in a string contains many params - java

I need pass a string parameter that contains many params. When receive the parameter, I use String.split() to split it to get all the params.
But one promblem accured. How to design my string decollator so that any ASCII CODE on keyboard can be passed correctly.
Hope for any advice.

Maybe you could have a look at variadic arguments instead of splitting a string. For example:
public void method(String... strings) {
// strings is actually an array
String firstParam = strings[0];
String secondParam = strings[1];
// ...
}
Calling:
method("string1");
method("string1", "string2", "string3");
// as many string args as you want

If I understood correctly - you need to encode set of parameters to one string. You can use some sequence of characters for this purpose, E.g.
final String delimiter = "###"
String value = "param1###param2###param3";
String[] parameters = value.split(delimiter);

Choose a character which is easy to enter and unlikely to appear in the input. Let's assume that character is #.
Normal input would like like Item 1#Item 2#Item 3. Actually, you can .trim() every item and let the user enter Item 1 # Item 2 # Item 3 if s/he prefers.
However, like you describe, say the user would like to enter Item #1, Item #2, etc.. There are a few ways to let him/her do this, but the easier is to let them escape the delimiter. For example, instead of Item #1 # Item #2 # Item #3, which would result in 6 different items being found normally, let the user enter, for example Item ##1 # Item ##2 # Item ##3. Then in your parsing, make sure to handle the case when two or more #'s have been entered in a row. split likely won't be good enough, you'll have to go through the string yourself.
Here's a sketch of a method which would split the input string for you:
private static List<String> parseArguments(String input) {
ArrayList<String> arguments = new ArrayList<String>();
String[] prelArguments = input.split("#");
for (int i = 0; i < prelArguments.length; i++) {
String argument = prelArguments[i];
if (argument.equals("")) {
// We will enter here if there were two or more #'s in a row
StringBuilder combinedArgument = new StringBuilder(arguments.remove(arguments.size() - 1));
int inARow = 0;
while (prelArguments[i+inARow].equals("")) {
inARow++;
combinedArgument.append('#');
}
i += inARow;
combinedArgument.append(prelArguments[i]);
arguments.add(combinedArgument.toString());
} else {
arguments.add(argument);
}
}
return arguments;
}
Error handling, edge-case handling and some performance improvement is missing from the above, but I think the idea comes through.

I would eliminate the problem, which is the misuse of String as an argument container. If you need to pass more parameters, pass more parameters. If this gets out of hand, consider passing a map, or a custom object that can contain all the parameters.

Related

How to retrieve a string array from a cursor

I have a cursor, called passableCursor, containing multiple fields, However one field,ingredients, holds multiple values and I'm unable to get all the values from it.
Example of retrieving from name
Log.d("recipeName", passableCursor.getString(passableCursor.getColumnIndex("name")));
Example of cursor structure
name-> name1
description -> description1
ingredients -> ingredient1, ingredient2, ingredient3.
I've been using getString for the name and description field. However, I'm struggling to get the values from ingredients. There doesn't seem to get a getStringArray method.
Here is one way:
SQLiteDatabase db =null;//Initialize this first
Cursor cursor = db.rawQuery("your query", null);//Replace with your query(e.g. SELECT FROM table WHERE something=2)
String preChanged = cursor.getString(cursor.getColumnIndex("row_name"));//Replace row name with your row name
String[] finalR = preChanged.split(",");//Can be changed to parts
//String[] finalR = new String[parts.length];
//for(int i = 0; i < parts.length; i++){
// finalR[i] = parts[i];//This for-loop is if you have special needs. If you want to convert the String to a different type(integer, boolean, etc), or you want to make sure it contains/does not contain something.
//}//Uncomment if needed
//Now, this needs a special compression method, to ensure correct format;
//raw is the raw array, continuing from up above we use finalR to make sure you as a reader understand the context
StringBuilder builder = new StringBuilder();
for (int i = 0; i < finalR.length; i++) {
if(i != finalR.length - 1)
builder.append(finalR[i] + ",");
else
builder.append(finalR[i]);
}
This can be converted into a series of methods as well.
Explanation of how this works:
1) Take raw array input(String in this case)
2) Create a single String, separating the different Strings with a ,. For longer Strings where there may be a comma, you may want to consider adding a backslash to the comma, and split at "\,"(two backslashes to make Java register it as an actual backslash, and not an escape char)
3) Save the String to your database
4) Load the String
5) It splits the string at ,, giving you several pieces. This is the array, and should be(assuming you have taken precautions protecting the system from in-string commas(,)
6) OPTIONAL: Refine the String array, by removing something, adding something, adding support for markup(replace [link text](http://example.com) with HTML code), change the type(string -> integer), whatever you need.
NOTE
This is the basic draft. This allows you to save basic arrays to a database, and recover it without having to create multiple rows. It may need refining, if you save in the String array text with commas, or any char that may attempt to cause SQL injection.
This can be refined to work with any other types of arrays, for an instance long, integer or boolean, but the saving is the same(it has to be compressed to a string), but you add .parse[Type](input) to convert it to an array of the desired type

Extracting Substrings from a List in Java

If I have a parent string (let's call it output) that contains a list of variable assignments like so ...
status.availability-state available
status.enabled-state enabled
status.status-reason The pool is available
And I want to extract the values of each variable in that list given the variable names, ie the substring after the space following status.availability-state, status.enabled-state, and status.status-reason, such that I end up with three different variable assignments making each of the following String comparisons true ...
String availability = output.substring(TODO);
String enabled = output.substring(TODO);
String reason = output.substring(TODO);
availability.equals("available");
enabled.equals("enabled");
reason.equals("The pool is available");
What is the simplest way to do this? Should I even use substring for this?
This is a little tricky because you need to assign the value to a specific variable - you can't just have a map of keys to variables in Java.
I would consider doing this with a switch:
for (String line : output.split('\n')) {
String[] frags = line.split(' ', 2); // Split the line in 2 at the space.
switch (frags[0]) { // This is the "key" of the variable.
case "status.availability-state":
availability = frags[1]; // This assigns the "value" to the relevant variable.
break;
case "status.enabled-state":
enabled = frags[1];
break;
// ... etc
}
}
It's not very pretty, but you don't have too many options.
There seem to be two questions here -- how to parse the string, and how to assign to variables by name.
Tackle the string parsing one step at a time:
first write a program to read one line at a time and output each one in the body of a loop. String.split() or StringTokenizer are two options here.
next enhance this by writing a method to handle one line. The same tools are helpful here, to split on spaces.
You should now have a program that can print name: status.availability-state, value: available for each line of input.
Next, you're asking to programatically assign to variables based on the name of the parameter.
There is no legitimate way to look at a variable's name at runtime (OK, Java 8 reflection has ways, but it shouldn't be used without very good reason).
So, the best you can do is to use a switch or if statement:
switch(name) {
case status.availability-state:
availability = value;
break;
... etc.
}
However, whenever you use switch or if you should think about whether there's a better way.
Is there any reason you can't turn these variables into Map entries?
configMap.add(name,value);
Then to read it:
doSomethingWith(configMap.get("status.availability");
That's what maps are for. Use them.
This is a similar situation to the rookie mistake of using variables called person1, person2, person3... instead of using an array. Eventually they ask "How do I go from the number 25 to my variable person25?" -- and the answer is, you can't, but an array or list makes it easy. people[number] or people.get(number)
A valid alternative is to split the string by \n and add to a Map. Example:
String properties = "status.availability-state available\nstatus.enabled-state enabled\nstatus.status-reason The pool is available";
Map<String, String> map = Arrays.stream(properties.split("\n"))
.collect(Collectors.toMap(s -> s.split(" ")[0], s -> s.split(" ", 2)[1]));
System.out.println(map.get("status.status-reason"));
Should output The pool is available
This loop will match and extract the variables, and you can then assign them as you see fit:
Pattern regex = Pattern.compile("status\\.(.*?)-.*? ([a-z]+)");
Matcher matcher = regex.matcher(output);
while (matcher.find()) {
System.out.println(matcher.group(1) + "=" + matcher.group(2));
}
status\\. matches "status."
(.*?) matches any sequence of characters but isn't greedy, and captures them
-.* matches dash, any chars, space
([a-z]+) matches any string of lower-case letters, and captures them
Here's one way to do it:
Map<String, String> properties = getProperties(propertiesString);
availability = properties.get("availability-state");
enabled = properties.get("enabled-state");
reason = properties.get("status-reason");
// ...
public void getProperties(String input) {
Map<String, String> properties = new HashMap<>();
String[] lines = output.split("\n");
for (String line : lines) {
String[] parts = line.split(" ");
int keyStartIndex = parts[0].indexOf(".") + 1;
int spaceIndex = parts[1].indexOf(" ");
string key = parts[0].substring(keyStartIndex, spaceIndex);
properties.put(key, parts[1]);
}
return properties;
}
This seems to be a bit more straight-forward, in terms of the code that's setting these values, as each value is set to exactly the value from the map, rather than iterating over some list of strings and seeing if it contains a particular value and doing different things based on that.
This is designed with the primary use-case being that the string is created at runtime in memory. If the properties are created in an external file, this code would still work (after creating the desired String in memory), but it may be a better idea to use either a Properties file, or perhaps a Scanner.

Java - How can I check for the next upcoming space in a string? (For IRC Bot)

Obviously, for an IRC bot, input is generated by a user typing a single string, usually with a command and a few arguments, each separated by a space. I am coding an IRC bot using Java and would like to parse arguments that might vary in character length and save them into multiple strings for use later. I would like to make a command that looks something like this:
bot.command argument1 argument2 argument3
and I want it to be so that if the message starts with bot.command, then user will store argument1, time will store argument2, and date will store argument3. The thing is, though, that argument1, argument2, and argument3 could vary in character length, so doing something like time = message.substring(33, message.length()) will stop reading at the end of the argument, but it won't always read the string where the argument3's text begins. I need to detect where the separator space is and start reading argument3 from one character after that. And I can't use If statements to determine what the arguments might be, because they could be anything. Here's a template, if the above paragraph isn't clear:
String message //= the IRC message and bot input
String user;
String time;
String date;
if (message.startsWith("bot.command")) {
user = message.substring(13, detect next space here and stop reading one character before);
time = message.substring(detect where previous space was and start one character after that, end before next space);
date = message.substring(detect where previous space was and start one character after that, message.length());
}
I hope that kind of illustrates what I'm trying to do. Thank you for your help!
I'd recommend using String.split, which breaks up a string into an array using a delimiter regular expression. In your case, you might do:
String[] args = message.split("\\s+");
if (args[0].equals("bot.command"))
{
user = args[1];
...
}
The reason I'm splitting on the pattern \s+ (matches one or more whitespace characters) instead of just a space is that this way the program won't crash if the arguments are separate by more than one space or by something like a tab.
Edit (Removed StringTokenizer example) -
Since StringTokenizer is apparently "legacy", I would use a Scanner like this -
String message = "BOT.command USER_X THIS_IS_A_TIME THIS_IS_A_DATE";
String user = null;
String time = null;
String date = null;
// Use toLowerCase - assuming it's case insensitive.
if (message.toLowerCase()
.startsWith("bot.command")) {
Scanner st = new Scanner(message);
if (st.hasNext()) {
st.next();
}
if (st.hasNext()) {
user = st.next();
}
if (st.hasNext()) {
time = st.next();
}
if (st.hasNext()) {
date = st.next();
}
}
System.out.printf(
"User = %s, Time = %s, Date = %s\n", user,
time, date);
Output is
User = USER_X, Time = THIS_IS_A_TIME, Date = THIS_IS_A_DATE
If you are taking user input where you are not sure about how many space separated input words will be entered, you can do the following:
String[] terms = message.split("\\s+");
for(String word1 : terms) {
// doSomething()
}
But in your case, you just need 3 arguments, hence the looping will be done 3 times in the for loop.

Copy a string splitted to another string

In some place of a class I have declared a temporal String variable:
String name;
Which I will use to store data from a text. The text have many fields with these two types of format:
Type: text/html
name=foo
For this case, I am particularly interested in the fields of the type name=foo
So, I breaked previously the lines of the text using split
String lines[] = text.split("\n");
And, again, I will use split to identify the fields of the type mentioned. In the code below, the while cycle stops where it detects a name=foo field, and prints the value of that field in the console.
int i = 0; // Counter for the while cycle
while (!(lines[i].split("=")[0].equals("name"))) {
i++;
if (lines[i].split("=")[0].equals("name")) // If the field is name...
System.out.println(lines[i].split("=")[1]); // Prints the value of the field
name = lines[i].split("=")[1]; // <-- My problem is here
}
My problem starts when I want to copy the value of the field to the String variable mentioned early, giving me an java.lang.ArrayIndexOutOfBoundsException.
I need that String to do something with it later. Any idea to safely copy the value of that field to a String variable?
Adding paranthesis to your if saves you from two problems:
if a line contains no = the whole String is in [0] and accessing [1] will result in said Exception
you are changing (overwriting) the variable name regardless of the condition
To please the compiler you may also want to intialize name to something like null.
int i = 0; // Counter for the while cycle
while (!(lines[i].split("=")[0].equals("name"))) {
i++;
if (lines[i].split("=")[0].equals("name")){ // If the field is name...
System.out.println(lines[i].split("=")[1]); // Prints the value of the field
name = lines[i].split("=")[1]; // <-- My problem is here
}
}
In your code:
String name;
name = lines[i].split("=")[1];
Here name will overwrite every time.
I think you are looking for something like this:
String names[];
String lines[] = text.split("\n");
names[] = new String[lines.length];
And inside you while loop do it like:
names[i] = lines[i].split("=")[1];
There are quite a few things to note about your code:
you probably miss {} after the if-statement and therefore update name every run of the while-loop
you access [1] without checking how many elements the split("=") yielded
you literally call split("=") 4 times on almost every line. Save CPU-time by introducing a temporary variable!
you can replace your while-loop by a for-loop which also finds name=value in the first line and does not "throw up" if name=value is not inside any of the lines (you don't check whether i is less than lines.length)
I left your comments inside my answer; feel free to remove them.
Variant a (using an index):
for (int i = 0; i < lines.length; i++) {
// Only split once and keep X=Y together in name=X=Y by specifying , 2
final String[] split = lines[i].split("=", 2);
if (split.length == 2 && split[0].equals("name")){ // If the field is name...
System.out.println(split[1]); // Prints the value of the field
name = split[1]; // <-- My problem is here
break; // no need to look any further
}
}
Variant b (using "for-each"):
for (String line : lines) {
// Only split once and keep X=Y together in name=X=Y by specifying , 2
final String[] split = line.split("=", 2);
if (split.length == 2 && split[0].equals("name")) { // If the field is name...
System.out.println(split[1]); // Prints the value of the field
name = split[1]; // <-- My problem is here
break; // no need to look any further
}
}
I suppose your problem is when you reach the last line or a line which doesn't contains a "=" sign. You are checking
!(lines[i].split("=")[0].equals("name"))
but then you add 1 to i, so maybe this condition now is false
if (lines[i].split("=")[0].equals("name"))
and you will get java.lang.ArrayIndexOutOfBoundsException here
name = lines[i].split("=")[1];
if the line doesn't contains a "=".
Try
if (lines[i].split("=")[0].equals("name")) { // If the field is name...
System.out.println(lines[i].split("=")[1]); // Prints the value of the field
name = lines[i].split("=")[1];
}

Reading a string with multiple options

I have String like ",yes,,,,,,,,,,,,," which says option2 is selected out of 15 options. Here, a comma , represents a option; if it is selected then some data will be there in place of option. I need to read this string and get the exact option selected value. In above it should be option2. How shall I do this?
I have 15 options in database from which selected data is replaced here and , in place none selected.
Or, looked at another way, there are 15 fields separated by commas. One field — in the example, the second field — has a non-empty value; the others are all empty. How can I determine the first field that is not empty?
Try String.split(",") - this will return String[]
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#split%28java.lang.String%29
public class Split {
public static void main(String [] args) {
String [] options = args[0].split(",",15);
for(int i = 0; i < options.length; i++) {
System.out.printf("option %d = [%s]\n", i, options[i]);
}
}
}
If I understand you correctly, you get a string of commas and between two commas there is some word like "Yes". Your task is to retrieve the index of that word, i.e., the number of commas (plus 1) before the word.
First of all, that encoding for an option is quite stupid, so if it lies in your responsibility, change it.
The simple solution is to count the commas before the word.
Something like this will do:
String s = ",yes,,,,"
for ( int i = 0 , len = s.length() ; i < len ; i++ )
if ( s.charAt(i) != ',' )
return i+1;
throw new Exception ("No option found");
Well, I assume you want to split the string on , like this:
String[] options = ",yes,,,,,,,,,,,,,".split(",");
String option2 = options[1]; //yields "yes"
However, why don't you use some more understandable markup, like option2=yes etc.?
Have a look at Apache Commons CLI for some library to support better options.
If your String always follows the format you specify, you can use String.split(). For example,
String[] split = ",yes,,,,,,,,,,,,,".split(",");
System.out.format("The chosen data is in Option %d and is '%s'%n", split.length, split[split.length - 1]);
prints
The chosen data is in Option 2 and is 'yes'

Categories