I am writing a program that allows users to input variable names that they can then use in other Strings. For example, if the user enters:
$token aslkdjfna98y
A mapping is created for key "token" and value "aslkdjfna98y". I then want to add this token variable in a URL by specifying that it should be swapped out using this syntax:
http://www.example.com/getThing?token=$^{token}
So here, I would like to swap $^{token} with my mapped value aslkdjfna98y.
I have tried various String.replace and String.replaceAll calls, however I am currently getting stuck in a loop - where it's known that the String contains the text $^{token}, but I cannot replace the text. Here is where I am struggling:
if (request.contains("$^{"))
{
//handle variables
for (String s : variables.keySet())
{
String str = String.format(Locale.US, "$^{%s}", s);
while(request.contains(str))
{
//Stuck Here
request = request.replace(String.format(Locale.US, "$^{%s}", s), variables.get(s));
}
}
}
This could ideally be simplified down to:
request.replaceAll(regex, str);
How can I correctly replace the characters, or how can I improve this to use replaceAll?
Enclose the String in \\Q and \\E. This switches off all special characters in Java regexes:
request = request.replace(String.format(Locale.US, "\\Q$^{%s}\\E", s), variables.get(s));
"$^{token}"
im confused whats suppose to be in the token field...
any letters/num?
"$^{[a-zA-Z0-9]*}"
Um.....
a certain amount(8) of letters/numbs.
"$^{[a-zA-Z0-9]{8}}"
depending on the language you are using you might need to escape { $ and ^
I was able to simplify the code down to this simple block:
if (variables.get(s) != null) {
request = request.replaceAll(Log.format("\\Q$^{%s}\\E", s), variables.get(s));
}
else {
Log.err("No variable \"%s\" set", s);
}
Related
I have an url in this format:
http://www.example.com/path?param1=value1¶m2=value2
I need a regex to match the path and params1 and params2 in any order but if param3 is present then I need it to fail so:
String str1 = "/path?param1=value1¶m2=value2"; // This will match
String str2 = "/path?param2=value2¶m1=value1"; // This will match
String str3 = "/path?param1=value1¶m2=value¶m3=value3"; // This will not match
So for I've tried using lookarounds to match the parameters but it is failing:
/path\?(?!param3)(?=param1=.*)(?=param2=.*)
Any thoughts?
P.D. For the curious I'm trying to match a specific URL from an AndroidManifest.xml file https://developer.android.com/guide/topics/manifest/data-element.html
Try this one out:
^(?!.*param3)(?=.*param1=)(?=.*param2=).*$
https://regex101.com/r/rI1lH5/1
If you want the path in as well, then
^\/path(?!.*param3)(?=.*param1=)(?=.*param2=).*$
This started as a comment and I got a little carried away. You can sanitize the query and see if it matches the parameters you need it to and avoid regex all together (if possible)
private boolean checkProperQueryString(String url, String[] requiredKeys){
try{
UrlQuerySanitizer sanitizer = new UrlQuerySanitizer(url);
// Check that you have the right number of parameters
List<UrlQuerySanitizer.ParameterValuePair> parameters =
sanitizer.getParameterList();
if(parameters == null || parameters.size() != requiredKeys.length)
return false;
// Check to make sure that the parameters you have are the
// correct ones
for(String key : requiredKeys){
if(TextUtils.isEmpty(sanitizer(getValue(key))
return false;
}
// We pass every test, success!
return true;
} catch(Exception e){
// Catch any errors (haven't tested this so not sure of errors)
e.printStackTrace();
return false;
}
}
You can then make the call doing something like this
boolean validUrl = checkProperQueryString(url, new String[]{"param1", "param2"});
This doesn't directly answer your question, again just too much for a comment :P
Let me know if this just adds confusion for anyone and I can remove it.
The regex provided by Michael works well but there is a glitch. It also evaluates newParam. So we should change that with:
^(?!.*(\\?|&)param3)(?=.*(\\?|&)param1=)(?=.*(\\?|&)param2=).*$
Basically we check if the parameter name starts with a ? or &. Also if you want to make a parameter optional then you can just put a ? at the end like:
(?!.*(\\?|&)param3)(?=.*(\\?|&)param1=)(?=.*(\\?|&)param2=)?.*$
In the above param2 is optional.
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.
I am reading a text file into my program, and having the user search for a string. How can I make this so its case-insensitive? Here is a snippet of my code:
while (str1.hasNextLine())
{
String line = str1.nextLine();
line = line.replace(";", " ");
if(line.contains(Val))
{
System.out.println(line);
}
}
Val is the string variable. It is the string that the user entered, and the string that, if found in the text file, will print out on the line. But I need it to be case-insensitive. For some reason when I use equals.IgnoreCase it doesn't work.
In this scenario, make everything a unified case, and compare.
if (line.toLowerCase().contains(Val.toLowerCase())) {
// logic
}
There are limitations on what contains can do. It only checks CharSequences and does so in a case-sensitive fashion. By introducing a common case, this eliminates the case sensitivity issue.
Using the second StringTokenizer constructor, write a method that
returns either the first or second token in the input string based on
the token argument. You will need to set custom delimiters which are
enclosed in a string but not separated by commas.
okay, so this is what i have so far..... i need help making the code so that it returns either the first or the second token depending on the users input.... right now i only know how to return either the first or the second token. ive tried making a while or and if statement but it always says i cant convert int to string, ive even tried type casting but it wouldnt let me do that either.... what can i do to make it return whichever token the user inputs?
String parseEqn_p2(String input, int token) {
StringTokenizer st = new StringTokenizer(input, "+-/*%");
String first = st.nextToken();
String second = st.nextToken();
return first ;
""it always says i cant convert int to string," - Use String.valueOf(int)
You probably want something like this
if (first.eqauls(String.valueOf(token)) {
return first;
} else if (...) {
}
return null; // if not found
...
Note : you may also need to .trim() the token, depending on what the input is. If there are spaces, the tokenizer won't exclude them, so you would need to call .trim() on the String first before trying to compare. like
String first = st.nextToken().trim();
Also, it looks like you may want to use a character class, because what you're doing is looking using a single delimiter of all the characters, which isn't what you want. Try this instead
StringTokenizer st = new StringTokenizer(input, "[+-/*%]");
In for any case you wanted the method to return an int, then you would need to parse the token
int first = Integer.parseInt(st.nextToken());
Also, NOTE: Whoever gave you this assignment, ask them to read the documentation for StringTokenizer, where you'll find this excerpt
StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead.
The following example illustrates how the String.split method can be used to break up a string into its basic tokens:
String[] result = "this is a test".split("\\s");
for (int x=0; x<result.length; x++)
System.out.println(result[x]);
I am reading in a csv file in Java and, depending on the format of the string on a given line, I have to do something different with it. The three different formats contained in the csv file are (using random numbers):
833
"79, 869"
"56-57, 568"
If it is just a single number (833), I want to add it to my ArrayList. If it is two numbers separated by a comma and surrounded by quotations ("79, 869)", I want to parse out the first of the two numbers (79) and add it to the ArrayList. If it is three numbers surrounded by quotations (where the first two numbers are separated by a dash, and the third by a comma ["56-57, 568"], then I want to parse out the third number (568) and add it to the ArrayList.
I am having trouble using str.contains() to determine if the string on a given line contains a dash or not. Can anyone offer me some help? Here is what I have so far:
private static void getFile(String filePath) throws java.io.IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String str;
while ((str = reader.readLine()) != null) {
if(str.endsWith("\"")){
if (str.contains(charDash)){
System.out.println(str);
}
}
}
}
Thanks!
I recommend using the version of indexOf that actually takes a char rather than a string, since this method is much faster. (It is a simple loop, without a nested loop.)
I.e.
if (str.indexOf('-')!=-1) {
System.out.println(str);
}
(Note the single quotes, so this is a char, rather than a string.)
But then you have to split the line and parse the individual values. At present, you are testing if the whole line ends with a quote, which is probably not what you want.
The following code works for me (note: I wrote it with no optimization in mind - it's just for testing purposes):
public static void main(String args[]) {
ArrayList<String> numbers = GetNumbers();
}
private static ArrayList<String> GetNumbers() {
String str1 = "833";
String str2 = "79, 869";
String str3 = "56-57, 568";
ArrayList<String> lines = new ArrayList<String>();
lines.add(str1);
lines.add(str2);
lines.add(str3);
ArrayList<String> numbers = new ArrayList<String>();
for (Iterator<String> s = lines.iterator(); s.hasNext();) {
String thisString = s.next();
if (thisString.contains("-")) {
numbers.add(thisString.substring(thisString.indexOf(",") + 2));
} else if (thisString.contains(",")) {
numbers.add(thisString.substring(0, thisString.indexOf(",")));
} else {
numbers.add(thisString);
}
}
return numbers;
}
Output:
833
79
568
Although it gets a lot of hate these days, I still really like the StringTokenizer for this kind of stuff. You can set it up to return the tokens and, at least to me, it makes the processing trivial without interacting with regexes
you'd have to create it using ",- as your tokens, then just kick it off in a loop.
st=new StringTokenizer(line, "\",-", true);
Then you set up a loop:
while(st.hasNextToken()) {
String token=st.nextToken();
Each case becomes it's own little part of the loop:
// Use punctuation to set flags that tell you how to interpret the numbers.
if(token == "\"") {
isQuoted = !isQuoted;
} else if(token == ",") {
...
} else if(...) {
...
} else { // The punctuation has been dealt with, must be a number group
// Apply flags to determine how to parse this number.
}
I realize that StringTokenizer is outdated now, but I'm not really sure why. Parsing regular expressions can't be faster and the syntax is--well split is a pretty sweet syntax I gotta admit.
I guess if you and everyone you work with is really comfortable with Regular Expressions you could replace that with split and just iterate over the resultant array but I'm not sure how to get split to return the punctuation--probably that "+" thing from other answers but I never trust that some character I'm passing to a regular expression won't do something utterly unexpected.
will
if (str.indexOf(charDash.toString()) > -1){
System.out.println(str);
}
do the trick?
which by the way is fastest than contains... because it implements indexOf
Will this work?
if(str.contains("-")) {
System.out.println(str);
}
I wonder if the charDash variable is not what you are expecting it to be.
I think three regexes would be your best bet - because with a match, you also get the bit you're interested in. I suck at regex, but something along the lines of:
.*\-.*, (.+)
.*, (.+)
and
(.+)
ought to do the trick (in order, because the final pattern matches anything including the first two).