I am attempting to get a value out of a partial JSON payload just using the "split" method. I can only use this method since this API is very limited. I can get my value using Pattern and match APIs..
package com.company;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {
public static void main(String[] args) {
// write your code here
String myString = "{\n" +
" \"8\": [\n" +
" {\n" +
" \"TEST\": \"LN17ELJ\",\n" +
" \"ROUTE_UNIQUE_ID_REFERENCE\": \"2172752\",\n" +
" \"ORDER_UNIQUE_ID_REFERENCE\": \"109197634\",\n" +
" \"STATUS\": \"HORLEY\",\n" +
" \"SECONDARY_NAV_CITY\": \"HORLEY\",\n" +
" \"ROUTE\": \"THE STREET 12\",\n";
String myRegexPattern = "\"([ROUTE_UNIQUE_ID_REFERENCE\"]+)\"\\s*:\\s*\"([^\"]+)\",?";
Pattern pattern = Pattern.compile(myRegexPattern);
Matcher matcher = pattern.matcher(myString);
if (matcher.find())
{
System.out.println(matcher.group(2));
} else {
System.out.println("Didn't work!");
}
}
}
However; When I try and using String.split it doesn't work and my value is not in any of the array indexes..
package com.company;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {
public static void main(String[] args) {
// write your code here
String myString = "{\n" +
" \"8\": [\n" +
" {\n" +
" \"TEST\": \"LN17ELJ\",\n" +
" \"ROUTE_UNIQUE_ID_REFERENCE\": \"2172752\",\n" +
" \"ORDER_UNIQUE_ID_REFERENCE\": \"109197634\",\n" +
" \"STATUS\": \"HORLEY\",\n" +
" \"SECONDARY_NAV_CITY\": \"HORLEY\",\n" +
" \"ROUTE\": \"THE STREET 12\",\n";
String myRegexPattern = "\"([ROUTE_UNIQUE_ID_REFERENCE\"]+)\"\\s*:\\s*\"([^\"]+)\",?";
String[] newValue = myString.split(myRegexPattern);
for(int i = 0; i < newValue.length; i++) {
if(newValue[i].equals("2172752")) {
System.out.println("IT'S HERE!");
}
}
}
}
What would be the best way to do this? Is there a better way to get ROUTE_UNIQUE_ID_REFERENCE with just using split??
Your regular expression isn't doing what you are expecting. It is not matching "ROUTE_UNIQUE_ID_REFERENCE":"...", but matching any key that starts with any of the letters in ROUTE_UNIQUE_ID_REFERENCE. You could replace it with something like \"ROUTE_UNIQUE_ID_REFERENCE[\"\\s:]+([^\",]+) which will match what you are after in matcher group 1.
The split function doesn't work as you expect. The regular expression you are using in the split is viewed as the delimiter. Thus it is removing the data you are hoping to extract.
Assuming you are looking to get all of the values for ROUTE_UNIQUE_ID_REFERENCE, in the case there is more than one ROUTE_UNIQUE_ID_REFERENCE in your real data, your first example is closer to what you are after.
You need to fix your regular expression and matching group
Use a while loop instead of an if statement to find all the instances
String myRegexPattern = "\"ROUTE_UNIQUE_ID_REFERENCE[\"\\s:]+([^\",]+)";
Pattern pattern = Pattern.compile(myRegexPattern);
Matcher matcher = pattern.matcher(myString);
while (matcher.find()) {
System.out.println(matcher.group(1));
}
Related
I have the following code to convert a camel-case phrase to sentence-case. It works fine for almost all cases, but it can't handle acronyms. How can this code be corrected to work with acronyms?
private static final Pattern UPPERCASE_LETTER = Pattern.compile("([A-Z]|[0-9]+)");
static String toSentenceCase(String camelCaseString) {
return camelCaseString.substring(0, 1).toUpperCase()
+ UPPERCASE_LETTER.matcher(camelCaseString.substring(1))
.replaceAll(matchResult -> " " + (matchResult.group(1).toLowerCase()));
}
JUnit5 test:
#ParameterizedTest(name = "#{index}: Convert {0} to sentence case")
#CsvSource(value = {"testOfAcronymUSA:Test of acronym USA"}, delimiter = ':')
void shouldSentenceCaseAcronym(String input, String expected) {
//TODO: currently fails
assertEquals(expected, toSentenceCase(input));
}
Output:
org.opentest4j.AssertionFailedError:
Expected :Test of acronym USA
Actual :Test of acronym u s a
I thought to add (?=[a-z]) to the end of the regex, but then it doesn't handle the spacing correctly.
I'm on Java 14.
Change the regex to (?<=[a-z])[A-Z]+|[A-Z](?=[a-z])|[0-9]+ where
(?<=[a-z])[A-Z]+ specifies positive lookbehind for [a-z]
[A-Z](?=[a-z]) specifies positive lookahead for [a-z]
Note that you do not need any capturing group.
Demo:
import java.util.regex.Pattern;
public class Main {
private static final Pattern UPPERCASE_LETTER = Pattern.compile("(?<=[a-z])[A-Z]+|[A-Z](?=[a-z])|[0-9]+");
static String toSentenceCase(String camelCaseString) {
return camelCaseString.substring(0, 1).toUpperCase() + UPPERCASE_LETTER.matcher(camelCaseString.substring(1))
.replaceAll(matchResult -> !matchResult.group().matches("[A-Z]{2,}")
? " " + matchResult.group().toLowerCase()
: " " + matchResult.group());
}
public static void main(String[] args) {
System.out.println(toSentenceCase("camelCaseString"));
System.out.println(toSentenceCase("USA"));
System.out.println(toSentenceCase("camelCaseStringUSA"));
}
}
Output:
Camel case string
USA
Camel case string USA
To fix your immediate issue you may use
private static final Pattern UPPERCASE_LETTER = Pattern.compile("([A-Z]{2,})|([A-Z]|[0-9]+)");
static String toSentenceCase(String camelCaseString) {
return camelCaseString.substring(0, 1).toUpperCase()
+ UPPERCASE_LETTER.matcher(camelCaseString.substring(1))
.replaceAll(m -> m.group(1) != null ? " " + m.group(1) : " " + m.group(2).toLowerCase() );
}
See the Java demo.
Details
([A-Z]{2,})|([A-Z]|[0-9]+) regex matches and captures into Group 1 two or more uppercase letters, or captures into Group 2 a single uppercase letter or 1+ digits
.replaceAll(m -> m.group(1) != null ? " " + m.group(1) : " " + m.group(2).toLowerCase() ) replaces with space + Group 1 if Group 1 matched, else with a space and Group 2 turned to lower case.
I am getting file names as string as follows:
file_g001
file_g222
g_file_z999
I would like to return files that contains "g_x" where x is any number (as string). Note that the last file should not appear as the g_ is followed by an alphabet and not a number like the first 2.
I tried: file.contains("_g[0-9]*$") but this didn't work.
Expected results:
file_g001
file_g222
Are you using the method contains of String ?
If so, it does not work with regular expression.
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#contains-java.lang.CharSequence-
public boolean contains(CharSequence s)
Returns true if and only if this string contains the specified sequence of char values.
Consider using the method matches.
https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#matches(java.lang.String)
Your regular expression is also fine, we'd just slightly improve that to:
^.*_g[0-9]+$
or
^.*_g\d+$
and it would likely work.
The expression is explained on the top right panel of this demo if you wish to explore/simplify/modify it.
Test
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final String regex = "^.*_g[0-9]+$";
final String string = "file_g001\n"
+ "file_g222\n"
+ "file_some_other_words_g222\n"
+ "file_g\n"
+ "g_file_z999";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);
while (matcher.find()) {
System.out.println("Full match: " + matcher.group(0));
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println("Group " + i + ": " + matcher.group(i));
}
}
I have the following json document:
{
"videoUrl":"",
"available":"true",
"movie":{
"videoUrl":"http..."
},
"account":{
"videoUrl":"http...",
"login":"",
"password":""
}
}
In this json I have a property named videoUrl, I want to get first non empty videoUrl
My regex:
("videoUrl":)("http.+")
But this regex match the following String
"videoUrl" :"http..."},
"account" : {"videoUrl" : "http...","login" : "","password" : ""
What is my way to write Regex that will find first non empty videoUrl with it's value
(Result should be "videoUrl":"http...")
Add (?!,) at the end of the regex, it will make the regex stop at an , without capturing it:
public static void main(String[] args) {
String input = "{ \n" +
" \"videoUrl\":\"\",\n" +
" \"available\":\"true\",\n" +
" \"movie\":{ \n" +
" \"videoUrl\":\"http...\"\n" +
" },\n" +
" \"account\":{ \n" +
" \"videoUrl\":\"http...\",\n" +
" \"login\":\"\",\n" +
" \"password\":\"\"\n" +
" }\n" +
"} ";
Pattern pattern = Pattern.compile("(\"videoUrl\":)(\"http.+\")(?!,)");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
System.out.println(matcher.group()); // "videoUrl":"http..."
}
}
It will be more appropriate to use one of JSON parsers, like Gson or Jackson, instead of regex. Something like:
String jsonStr = "...";
Gson gson = new Gson();
JsonObject json = gson.fromJson(jsonStr, JsonObject.class);
String url = element.get("videoUrl").getAsString();
public static void main(String[] args) {
String text = "hi ravi \"how are you\" when are you coming";
String regex = "\"([^\"]*)\"|(\\S+)";
Matcher m = Pattern.compile(regex).matcher(text);
while (m.find()) {
if (m.group(1) != null) {
System.out.println("Quoted [" + m.group(1) + "]");
} else{
System.out.println("Plain [" + m.group(0) + "]");
}
}
// getSplits(text);
}
Output:
Plain [hi]
Plain [ravi]
Quoted [how are you]
Plain [when]
Plain [are]
Plain [you]
Plain [coming]
Above code is working fine if the given text has only one single quotation. Can any one help me how to get below output with below input:
text = "hi ravi \"\"how are\" you\" when are you coming";
Expected Output:
Plain [hi]
Plain [ravi]
Quoted ["how are" you]
Plain [when]
Plain [are]
Plain [you]
Plain [coming]
Following regex works for your example input/output. You will have to give a more detailed description of the expected result, as this might not be what you were expecting.
public static void main(String[] args) {
String text = "hi ravi \"\"how are\" you\" when are you coming";
String regex = "(\".+\")|(\\S+)";
Matcher m = Pattern.compile(regex).matcher(text);
while (m.find()) {
if (m.group(1) != null) {
System.out.println("Quoted [" + m.group(1) + "]");
} else{
System.out.println("Plain [" + m.group(0) + "]");
}
}
// getSplits(text);
}
This will do:
[\t]+(?=([^"]*"[^"]*")*[^"]*$)
See the DEMO
I have a string, let's call it output, that's equals the following:
ltm data-group internal str_testclass {
records {
baz {
data "value 1"
}
foobar {
data "value 2"
}
topaz {}
}
type string
}
And I'm trying to extract the substring between the quotes for a given "record" name. So given foobar I want to extract value 2. The substring I want to extract will always come in the form I have prescribed above, after the "record" name, a whitespace, an open bracket, a new line, whitespace, the string data, and then the substring I want to capture is between the quotes from there. The one exception is when there is no value, which will always happen like I have prescribed above with topaz, in which case after the "record" name there will just be an open and closed bracket and I'd just like to get an empty string for this. How could I write a line of Java to capture this? So far I have ......
String myValue = output.replaceAll("(?:foobar\\s{\n\\s*data "([^\"]*)|()})","$1 $2");
But I'm not sure where to go from here.
Let's start extracting "records" structure with following regex ltm\s+data-group\s+internal\s+str_testclass\s*\{\s*records\s*\{\s*(?<records>([^\s}]+\s*\{\s*(data\s*"[^"]*")?\s*\}\s*)*)\}\s*type\s*string\s*\}
Then from "records" group, just find for sucessive match against [^\s}]+\s*\{\s*(?:data\s*"(?<data>[^"]*)")?\s*\}\s*. The "data" group contains what's you're looking for and will be null in "topaz" case.
Java strings:
"ltm\\s+data-group\\s+internal\\s+str_testclass\\s*\\{\\s*records\\s*\\{\\s*(?<records>([^\\s}]+\\s*\\{\\s*(data\\s*\"[^\"]*\")?\\s*\\}\\s*)*)\\}\\s*type\\s*string\\s*\\}"
"[^\\s}]+\\s*\\{\\s*(?:data\\s*\"(?<data>[^\"]*)\")?\\s*\\}\\s*"
Demo:
String input =
"ltm data-group internal str_testclass {\n" +
" records {\n" +
" baz {\n" +
" data \"value 1\"\n" +
" }\n" +
" foobar {\n" +
" data \"value 2\"\n" +
" }\n" +
" topaz {}\n" +
" empty { data \"\"}\n" +
" }\n" +
" type string\n" +
"}";
Pattern language = Pattern.compile("ltm\\s+data-group\\s+internal\\s+str_testclass\\s*\\{\\s*records\\s*\\{\\s*(?<records>([^\\s}]+\\s*\\{\\s*(data\\s*\"[^\"]*\")?\\s*\\}\\s*)*)\\}\\s*type\\s*string\\s*\\}");
Pattern record = Pattern.compile("(?<name>[^\\s}]+)\\s*\\{\\s*(?:data\\s*\"(?<data>[^\"]*)\")?\\s*\\}\\s*");
Matcher lgMatcher = language.matcher(input);
if (lgMatcher.matches()) {
String records = lgMatcher.group();
Matcher rdMatcher = record.matcher(records);
while (rdMatcher.find()) {
System.out.printf("%s:%s%n", rdMatcher.group("name"), rdMatcher.group("data"));
}
} else {
System.err.println("Language not recognized");
}
Output:
baz:value 1
foobar:value 2
topaz:null
empty:
Alernatives: As your parsing a custom language, you can give a try to write an ANTLR grammar or create Groovy DSL.
Your regex shouldn't even compile, because you are not escaping the " inside your regex String, so it is ending your String at the first " inside your regex.
Instead, try this regex:
String regex = key + "\\s\\{\\s*\\n\\s*data\\s*\"([^\"]*)\"";
You can check out how it works here on regex101.
Try something like this getRecord() method where key is the record 'name' you're searching for, e.g. foobar, and the input is the string you want to search through.
public static void main(String[] args) {
String input = "ltm data-group internal str_testclass { \n" +
" records { \n" +
" baz { \n" +
" data \"value 1\" \n" +
" } \n" +
" foobar { \n" +
" data \"value 2\" \n" +
" }\n" +
" topaz {}\n" +
" } \n" +
" type string \n" +
"}";
String bazValue = getRecord("baz", input);
String foobarValue = getRecord("foobar", input);
String topazValue = getRecord("topaz", input);
System.out.println("Record data value for 'baz' is '" + bazValue + "'");
System.out.println("Record data value for 'foobar' is '" + foobarValue + "'");
System.out.println("Record data value for 'topaz' is '" + topazValue + "'");
}
private static String getRecord(String key, String input) {
String regex = key + "\\s\\{\\s*\\n\\s*data\\s*\"([^\"]*)\"";
final Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
//if we find a record with data return it
return matcher.group(1);
} else {
//else see if the key exists with empty {}
final Pattern keyPattern = Pattern.compile(key);
Matcher keyMatcher = keyPattern.matcher(input);
if (keyMatcher.find()) {
//return empty string if key exists with empty {}
return "";
} else {
//else handle error, throw exception, etc.
System.err.println("Record not found for key: " + key);
throw new RuntimeException("Record not found for key: " + key);
}
}
}
Output:
Record data value for 'baz' is 'value 1'
Record data value for 'foobar' is 'value 2'
Record data value for 'topaz' is ''
You could try
(?:foobar\s{\s*data "(.*)")
I think the replaceAll() isn't necessary here. Would something like this work:
String var1 = "foobar";
String regex = '(?:' + var1 + '\s{\n\s*data "([^"]*)")';
You can then use this as your regex to pass into your pattern and matcher to find the substring.
You can simple transform this into a function so that you can pass variables into it for your search string:
public static void SearchString(String str)
{
String regex = '(?:' + str + '\s{\n\s*data "([^"]*)")';
}