Can you help me to understand why my code doesn't work, please?
I am trying to get values from 2 columns from my database and store them in a hashmap where K_PARAM is my key and L_PARAM is my value. Then I would like to compare 2 characters from a line that I am extracting and see if these 2 characters are equals to my key or not. In case they are equals, I replace key with value.
Thanks in advance. This is the code :
if (action.equals("RP")) {
if (marqueCarte = null) {
jdbcTemplate.query(" select K_PARAM, L_PARAM from DLCOA.DLC_ADM_PARAMS where K_CHX_PARAM = '50'", new ResultSetExtractor<Map>(){
#Override
public Map extractData(ResultSet rs) throws SQLException,DataAccessException {
HashMap<String,String> marqueCarte = new HashMap<String,String>();
while (rs.next()) {
marqueCarte.put(rs.getString("K_PARAM"),rs.getString("L_PARAM"));
if (line.contains("blocE")) {
if (line.substring(line.indexOf("blocE") + 15, line.indexOf("blocE") + 15 + (line.substring(line.indexOf("blocE")+15)).indexOf("#")).equals(rs.getString("K_PARAM"))){
line = line.replace(line.substring(line.indexOf("blocE") + 15, line.indexOf("blocE") + 15 + (line.substring(line.indexOf("blocE")+15)).indexOf("#")),rs.getString("L_PARAM") );
}
}
}
return marqueCarte;
}
}
}
}
I got a more readable and modifiable solution for your second problem.
(I'm still not sure what's your first one)
Using regex and patterns you can achieve the replacement you want.
Let's assume that you are searching for the text "blocE" followed by 15 characters, followed at the same time by the text contained in rs.getString("K_PARAM") plus an "#"
We can model what you search as a pattern like this
"(blocE)(.{15})(" + key + "#)"
Parenthesis allow us to establish different groups in the regex.
Group 1 - blocE
Group 2 - 15 characters
Group 3 - key + #
Being group 0 the complete matching expression.
Knowing this you can do the replacement applying the following code
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestRegex {
public static void main(String[] args) {
String key = "KEY"; // rs.getString("K_PARAM")
String value = "VALUE"; // rs.getString("L_PARAM")
Pattern pattern = Pattern.compile("(blocE)(.{15})(" + key + "#)");
String input ="helloworldblocE111111111111111KEY#blocE111111111111111KEY";
Matcher m = pattern.matcher(input);
if (m.find()) {
String text2replace = m.group(0);
String replacement = m.group(1) + m.group(2) + value;
System.out.println(input.replaceFirst(text2replace, replacement));
}
}
}
If your pattern changes, you only have to change one line and you do not have to worry about such quantity of indexOf.
Related
I need to capture two groups from an input string. The values differ in structure as they come in.
The following are examples of the incoming strings:
Comment = "This is a comment";
NumericValue = 123456;
What I am trying to accomplish is to capture the string value from the left of the equals sign as one group and the value after the equals sign as a second group. The semicolon should never be included.
The caveat is that if the second group is a string, the quotes from each end must not be included in that capture group.
The expected results would be:
Comment = "This is a comment";
key group => Comment
value group => This is a comment
NumericValue = 123456;
key group => NumericValue
value group => 123456
The following is what I have so far. This works fine for capturing the numeric value, but leaves the end double quote when capturing the string value.
(?<key>\w+)\s*=\s*(?:[\"]?)(?<group>.+(?:(?=[\"]?;)))
EDIT
When applying the regex against a string value, it must allow capture of semicolons and double quotes within the string and ignore only the closing ones.
So, if we have an input of:
Comment = "This is a "comment"; This is still a comment";
The second capture group should be:
This is a "comment"; This is still a comment
An option is to use an alternation where you would have to check for group 2 or group 3:
(?<key>\w+)\h*=\h*(?:"(.*?)"|([^"\r\n]+));$
(?<key>\w+) Group key match 1+ word chars
\h*=\h* Match an = between optional horizontal whitespace chars
(?: Non capturing group
"(.+?)" Capture in group 2 1+ times any char between "
| Or
([^"\r\n]+) Capture group 3, match 1+ times any char except " or a newline
); Close non capturing group and match ;
$ End of string
Regex demo
In Java
String regex = "(?<key>\\w+)\\h*=\\h*(?:\"(.*?)\"|([^\"\\r\\n]+));$";
Edited based on comment to include ; and " in the comments as per the examples given:
(?<key>\w+)\s*=\s*(?:[\"]?)(?<value>((")(?!;?$)|;(?!$)|[^;"])+)"?;?$
The following one additionally doesn't allow ; or " to appear in the numeric text. However, to include this, I had to rename the capturing groups because the name cannot be used for more than one group.
(?<key>\w+)\s*=\s*((?:")(?<valueT>((")(?!;?$)|;(?!$)|[^;"])+)";?$|(?<valueN>[^;"]+);?$)
Here is a class that tests it.
For readability, I have separated the key and value regexes in the class. I have added the test cases in a method within the class. However, this still doesn't handle the case of a numeric text containing ; or ". Also, the line needs to be trimmed before being subjected to the pattern test (which I think is feasible).
public class NameValuePairRegex{
public static void main( String[] args ){
String SPACE = "\\s*";
String EQ = "=";
String OR = "|";
/* The original regex tried by you (for comparison). */
String orig = "(?<key>\\w+)\\s*=\\s*(?:[\\\"]?)(?<value>.+(?:(?=;)))";
String key = "(?<key>\\w+)";
String valuePatternForText = "(?:\")(?<valueT>((\")(?!;?$)|;(?!$)|[^;\"])+)\";?$";
String valuePatternForNumbers = "(?<valueN>[^;\"]+);?$";
String p = key + SPACE + EQ + SPACE + "(" + valuePatternForText + OR + valuePatternForNumbers + ")";
Pattern nvp = Pattern.compile( p );
System.out.println( nvp.pattern() );
print( input(), nvp );
}
private static void print( List<String> input, Pattern ep ) {
for( String e : input ) {
System.out.println( e );
Matcher m = ep.matcher( e );
boolean found = m.find();
if( !found ) {
System.out.println( "\t\tNo match" );
continue;
}
String valueT = m.group( "valueT" );
String valueN = m.group( "valueN" );
System.out.print( "\t\t" + m.group( "key" ) + " -> " + ( valueT == null ? "" : valueT ) + " " + ( valueN == null ? "" : valueN ) );
System.out.println( );
}
}
private static List<String> input(){
List<String> neg = new ArrayList<>();
Collections.addAll( neg,
"Comment = \"This is a comment\";",
"Comment = \"This is a comment with semicolon ;\";",
"Comment = \"This is a comment with semicolon ; and quote\"\";",
"Comment = \"This is a comment\"",
"Comment = \"This is a \"comment\"; This is still a comment\";",
"NumericValue = 123456;",
"NumericValue = 123;456;",
"NumericValue = 123\"456;",
"NumericValue = 123456" );
return neg;
}
}
Original answer:
The following changed regex is fulfilling the requirements you mentioned. I added the exclusion of ; and " from the value part.
Original that you tried:
(?<key>\w+)\s*=\s*(?:[\"]?)(?<group>.+(?:(?=[\"]?;)))
The changed one:
(?<key>\w+)\s*=\s*(?:[\"]?)(?<value>[^;"]+)
Regular expressions are fun, but look how clean and easy to read this would be without using a regular expression:
int equals = s.indexOf('=');
String key = s.substring(0, equals).trim();
String value = s.substring(equals + 1).trim();
if (value.endsWith(";")) {
value = value.substring(0, value.length() - 1).trim();
}
if (value.startsWith("\"") && value.endsWith("\"")) {
value = value.substring(1, value.length() - 1);
}
Don’t assume that because this uses more lines of code than a regular expression that it’s slower. The lines of code executed internally by a regex engine will far exceed the above code.
I have a String:
String s="<p>Dear <span>{customerName}, your {accountName} is actived </span></p><p> </p><p><span>Congrats!.....</span></p>";
So I want to take CustomerName and accountName words and replace with customers details. Can anyone please tell me how can I replace. Here customerName and accountName are dynamically changing ..because those are columns in database sometimes different columns. So i want to find the words within the { and } and need to replace with column data.
Use the following code
s = s.replace("{customerName}", realCustomerName);
s = s.replace("{accountName}", realAccountNAme);
With String's replace function, the first argument is the string you want to replace, and the second argument is the string you want to insert.
Try:
s=s.replace('{customerName}',CustomerName ).replace('{accountName}',accountName);
where CustomerName and accountName will be the strings holding your customers details
If you simply want to replace the words, you could do the following:
String s="<p>Dear <span>{customerName}, your {accountName} is actived </span></p><p> </p><p><span>Congrats!.....</span></p>";
s.replace( "{customerName}", customer.getName() );
s.replace( "{accountName}", account.getName() );
Or, if you are building the string yourself and you can modify it, it might be better to do the following:
String s="<p>Dear <span>%1$s, your %1$s is actived </span></p><p> </p><p><span>Congrats!.....</span></p>";
// You may also just create a new String object...
s = String.format( s, customer.getName(), account.getName() );
Finally, I found the answer to replace the words using regular expressions. Here words b/w ~ need to replace and these words are not fixed and dynamically will be added to string from UI text Area.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularEx {
/**
* #param args
*/
public static void main(String args[]) {
Pattern pattern = Pattern.compile("\\~.*?\\~");
StringBuilder s = new StringBuilder(
"~ABCD~~BBCc~All the best ~ABCD~~BBCc~~in~~Raja~ Such kind of people ~in~~Raja~~ABCD~~BBCc~~in~~Raja~rajasekhar~ABCD~~BBCc~~in~~Raja~ Bayanapalli ~Chinthalacheruvu~");
Matcher matcher = pattern.matcher(s);
// using Matcher find(), group(), start() and end() methods
String s1 =new String("~ABCD~~BBCc~All the best ~ABCD~~BBCc~~in~~Raja~ Such kind of people ~in~~Raja~~ABCD~~BBCc~~in~~Raja~rajasekhar~ABCD~~BBCc~~in~~Raja~ Bayanapalli ~Chinthalacheruvu~");
int i = 0;
while (matcher.find()) {
String grp = matcher.group();
int si = matcher.start();
int ei = matcher.end();
System.out.println("Found the text \"" + grp
+ "\" starting at " + si + " index and ending at index " + ei);
s1=s1.replaceAll(grp, "Raja");
//System.out.println("FinalString" + s1);
}
System.out.println("------------------------------------\nFinalString" + s1);
}
}
s = s.replace("{customerName}", "John Doe");
s = s.replace("{accountName}", "jdoe");
INPUT
Input can be in any of the form shown below with following mandatory content TXT{Any comma separated strings in any format}
String loginURL = "http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}";
String loginURL1 = "http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&TXT{UE-IP,UE-Username,UE-Password}";
String loginURL2 = "http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}&username=abcd&location={LOCATION}&password={PASS}";
String loginURL3 = "http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}";
String loginURL4 = "http://ip:port/path?username=abcd&password={PASS}";
Required Output
1. OutputURL corresponding to loginURL.
String outputURL = "http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}";
String outputURL1 = "http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}";
String outputURL2 = "http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}";
String outputURL3 = "http://ip:port/path?";
String outputURL4 = "http://ip:port/path?username=abcd&password={PASS}";
2. Deleted pattern(if any)
String deletedPatteren = TXT{UE-IP,UE-Username,UE-Password}
My Attempts
String loginURLPattern = TXT+"\\{([\\w-,]*)\\}&*";
System.out.println("1. ");
getListOfTemplates(loginURL, loginURLPattern);
System.out.println();
System.out.println("2. ");
getListOfTemplates(loginURL1, loginURLPattern);
System.out.println();
private static void getListOfTemplates(String inputSequence,String pattern){
System.out.println("Input URL : " + inputSequence);
Matcher templateMatcher = Pattern.compile(pattern).matcher(inputSequence);
if (templateMatcher.find() && templateMatcher.group(1).length() > 0) {
System.out.println(templateMatcher.group(1));
System.out.println("OutputURL : " + templateMatcher.replaceAll(""));
}
}
OUTPUT obtained
1.
Input URL : http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}
UE-IP,UE-Username,UE-Password}&password={PASS
OutputURL : http://ip:port/path?username=abcd&location={LOCATION}&
2.
Input URL : http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&TXT{UE-IP,UE-Username,UE-Password}
UE-IP,UE-Username,UE-Password
OutputURL : http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&
DRAWBACK OF ABOVE PATTERN
If i add any String containing character like #,%,# in between TXT{} then my code breaks.
How can i achieve it using java.util.regex library so that user can input any comma separated String between TXT{Any Comma Separated Strings}.
I would recommend using Matcher.appendReplacement:
public static void main(final String[] args) throws Exception {
final String[] loginURLs = {
"http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}",
"http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&TXT{UE-IP,UE-Username,UE-Password}",
"http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}&username=abcd&location={LOCATION}&password={PASS}",
"http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}",
"http://ip:port/path?username=abcd&password={PASS}"};
final Pattern patt = Pattern.compile("(\\?)?&?(TXT\\{[^}]++})(&)?");
for (final String loginURL : loginURLs) {
System.out.printf("%1$-10s %2$s%n", "Processing", loginURL);
final StringBuffer sb = new StringBuffer();
final Matcher matcher = patt.matcher(loginURL);
while (matcher.find()) {
final String found = matcher.group(2);
System.out.printf("%1$-10s %2$s%n", "Found", found);
if (matcher.group(1) != null && matcher.group(3) != null) {
matcher.appendReplacement(sb, "$1");
} else {
matcher.appendReplacement(sb, "$3");
}
}
matcher.appendTail(sb);
System.out.printf("%1$-10s %2$s%n%n", "Processed", sb.toString());
}
}
Output:
Processing http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}
Found TXT{UE-IP,UE-Username,UE-Password}
Processed http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}
Processing http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&TXT{UE-IP,UE-Username,UE-Password}
Found TXT{UE-IP,UE-Username,UE-Password}
Processed http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}
Processing http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}&username=abcd&location={LOCATION}&password={PASS}
Found TXT{UE-IP,UE-Username,UE-Password}
Processed http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}
Processing http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}
Found TXT{UE-IP,UE-Username,UE-Password}
Processed http://ip:port/path
Processing http://ip:port/path?username=abcd&password={PASS}
Processed http://ip:port/path?username=abcd&password={PASS}
As you rightly point out, there are 3 possible cases:
"?{TEXT}&" -> "?"
"&{TEXT}&" -> "&"
"?{TEXT}" -> ""
So what we need to do is test for those cases in the regex. Here is the pattern:
(\\?)?&?(TXT\\{[^}]++})(&)?
Explanation:
(\\?)? optionally matches and captures a ?
&? optionally captures an &
(TXT\\{[^}]++}) matches and captures TXT, followed by {, followed by one or most not } (possessively), followed by } (closing brackets don't need to be escaped
(&)? optionally matches and captures a &
We have 3 groups:
potentially a ?
the required text
potentially an &
Now when we find a match we need to replace with the appropriate capture of case 1..3
if (matcher.group(1) != null && matcher.group(3) != null) {
matcher.appendReplacement(sb, "$1");
} else {
matcher.appendReplacement(sb, "$3");
}
If groups 1 and 3 are both present:
We must be in case 1; we must replace with "?" which is in group 1 so $1.
Otherwise we are in case 2 or 3:
In case 2 we need to replace with "&" and in 3 with "".
In case 2 group 3 will hold "&" and in case 3 it will hold "" so we can replace with $3 in both these cases.
Here I only capture the TXT{...} part using a match group. This means that although the leading ? or & is replaced it is not in the String found. I you only want the bit between {} then just move the parenthesis.
Note that I reuse the Pattern - you can also reuse the Matcher if performance is a concern. You should always reuse the Pattern as it is (very) expensive to create. Store it in a static final if you can - it's threadsafe, matchers are not. The usual way to do it is to store the Pattern in a static final and then reuse the Matcher in the context of a method.
Also, the use of Matcher.appendReplacement is much more efficient than your current approach as it only needs to process the input once. Your approach parses the string twice.
I have a program where I want to filter Strings with a set number of "+"'s at the beginning.
For example:
+++Adam is working very well.
++Adam is working well.
+Adam is doing OK.
How do I only pick up each particular case (i.e. only one plus sign, only two plus signs, only three plus signs)? I usually get a return of anything beginning with a +.
I have the following regex patterns compiled, but I either get only one return (usually the two ++) or all of them:
public static String regexpluschar = "^\\Q+\\E{1}[\\w <]";
public static String regexpluspluschar = "^\\Q+\\E{2}[\\w <]";
public static String regexpluspluspluschar = "^\\Q+\\E{3}[\\w <]";
Pattern plusplusplus = Pattern.compile(regexpluspluspluschar);
Pattern plusplus = Pattern.compile(regexpluspluschar);
Pattern plus = Pattern.compile(regexpluschar);
I then try to find using a Matcher class - I've used .find() and .matches() but don't get the result I'm after (java+regex newbie alert here).
Matcher matcherplusplusplus = plusplusplus.matcher(check);
Matcher matcherplusplus = plusplus.matcher(check);
Matcher matcherplus = plus.matcher(check);
//OK we have 3+'s
if ((matcherplusplusplus.find())==true){
System.out.println("Filtering 3 +s.");
System.out.println("filter is " + filter + " in the 3 + filter.");
String toChange = getItem(i);
setItemFiltered(i, toChange);
}
//OK - we have 2 +'s
if ((matcherplusplus.find())==true){
System.out.println("Filtering 2 +s.");
System.out.println("filter is " + filter + " in the 2 + filter.");
String toChange = getItem(i);
setItemFiltered(i, toChange);
}
//OK - we have 1 +'s
if ((matcherplus.find())==true){
System.out.println("Filtering 1 +.");
System.out.println("filter is " + filter + " in the 1 + filter.");
String toChange = getItem(i);
setItemFiltered(i, toChange);
}
For the very curious, the above if's are embedded in a for loop that cycles around some JTextFields. Full code at: http://pastebin.ca/2199327
Why not simpler :
public static String regexpluschar = "^\\+[\\w <]";
public static String regexpluspluschar = "^\\+{2}[\\w <]";
public static String regexpluspluspluschar = "^\\+{3}[\\w <]";
or even
public static String regexpluschar = "^\\+[^\\+]";
public static String regexpluspluschar = "^\\+{2}[^\\+]";
public static String regexpluspluspluschar = "^\\+{3}[^\\+]";
Edit : It's working on my test program, but I had to removed your specific code :
String toChange = getItem(i);
setItemFiltered(i, toChange);
proof : my output is :
Filtering 3 +s.
+++Adam is working very well. is in the 3 + filter.
Filtering 2 +s.
++Adam is working well. is in the 2 + filter.
Filtering 1 +.
+Adam is doing OK. is in the 1 + filter.
Your filter is working, but you specific code may not... (maybe have a look at setItemFiltered?)
I was thinking something like this would be easier:
public static void main(String[] args) {
Pattern pattern = Pattern.compile("^(\\+{1,3}).*");
Matcher matcher = pattern.matcher(<your text>);
if (matcher.matches()) {
String pluses = matcher.group(1);
switch (pluses.length()) {
}
}
}
And if you want to be sure that ++++This is insane does not match then change the pattern to
Pattern pattern = Pattern.compile("^(\\+{1,3})[^+].*");
Compare how you would accomplish the two tasks mentioned below with and without regular expressions. The problem:
The format for an SMS-based food delivery will be:
PABUSOG slash or comma repeated an infinite number of times #
// The quantity can only be numeric. For simplicity, assume that quantity is always an integer
e.g. PABUSOG STRFRY_SMAI/2 HSHBRWN_BRGR/1 COFEEFLT/1 #En311
it will capture the following:
STRFRY_SMAI - 2
HSHBRWN_BRGR - 1
COFEEFLT - 1
this is my sample code: // doing with regex
String message = "PABUSOG ASD_ASD/1 ASD_ASA/2";
Pattern pattern = Pattern.compile("PABUSOG(\\s+([A-Z]+_[A-Z]+)(/|,)([0-9]))+"
,Pattern.CASE_INSENSITIVE);
Matcher m = pattern.matcher(message);
try
{
if (m.matches())
{
String food = m.group(2);
String quantity = m.group(4);
System.out.println(food + " -- " + quantity + "\\n");
}
}
catch (NullPointerException e)
{
}
it displays the ASD_ASA -- 2, it overrides the 1st one which is ASD_ASD/1.
it must display
ASD_ASD -- 1
ASD_ASA -- 2
You cannot accomplish that with a single regex giving you all the data inside groups. And there's no great need for complex regex either. But still if you prefer regex try searching for pattern iteratively.
if (!message.startsWith("PABUSOG")) {
return;
}
Pattern pattern = Pattern.compile("([A-Z_]+)[/,]([0-9])+", Pattern.CASE_INSENSITIVE);
Matcher m = pattern.matcher(message);
while (m.find()) {
String food = m.group(1);
String quantity = m.group(2);
System.out.println(food + " -- " + quantity);
}
Without complex regex you can do the following by using String API:
// Check for correct header
if (!message.startsWith("PABUSOG")) {
return;
}
// split by whitespaces
String[] items = message.split("\\s+");
// skip header and iterate over remaining items
for (String item : Arrays.asList(items).subList(1, items.length)) {
// split each item by / or ,
String[] foodQuantity = item.split("[/,]");
assert foodQuantity.length == 2;
String food = foodQuantity[0];
String quantity = foodQuantity[1];
System.out.println(food + " -- " + quantity);
}
To skip items started with # you can either add
if (item.startsWith("#")) {
break; // or continue if it can be not the last
}
inside loop or limit subList in the following way if you sure that such item is always present and terminates the sequence: Arrays.asList(items).subList(1, items.length - 1).
By the way, your pattern [A-Z]+_[A-Z]+ won't match COFEEFLT from your example.