2

I have two types of patterns, as below:

  • Type I: ${<varName>}
  • Type II: $${<varName>}

The patterns can be found standing alone, or contained multiple matches in a string. I need to find the occurrences of these patterns, so I wrote a query to search by matching regex. However, the problem is that for any Type II pattern, they themselves contain a match for Type I pattern. For example, $${newVar} will be detected twice as $${newVar} and ${newVar}. I only want the former to be returned. The regular expressions I used are:

  • Type I: \$\{[a-zA-Z0-9]+\}
  • Type II: \$\$\{[a-zA-Z0-9]+\}

You can see an example of the detected string here (below)

enter image description here

Note that the second detection is correct, whereas the first detection is unwanted.

Is there anyway too modify these regular expressions to meet my need? Or is there any alternatives? Please feel free to suggest. All answers are welcome!! Thank you all.

Java Devil
  • 10,629
  • 7
  • 33
  • 48
mrawesome
  • 33
  • 4
  • Shouldn't your Type II be `\$\$\{[a-zA-Z0-9]+\}`? And for Type I, couldn't you have `[^\$]\$\{[a-zA-Z0-9]+\}`? – AntonH Jun 08 '17 at 02:55
  • Apologize for the typo, it is ```\$\$```. For your suggested regex, I've tried, and it'll match whatever character in front of the pattern, for example ```fasdfasd'f${var}'``` (```'``` denotes the match) – mrawesome Jun 08 '17 at 02:59
  • You're right, I forgot that it will be included in the capture. It might need a lookahead, but I'm not good enough with those to give a solution. – AntonH Jun 08 '17 at 03:05

2 Answers2

2

It seems you need to find the occurrences of both Type I and Type II patterns, so you should do that in one scan.

That can be done like this:

String input = "adklsfjb$${xxx}dklsjfnsdklj${yyy}";

Pattern p = Pattern.compile("(\\$)?\\$\\{([^}]+)}");
for (Matcher m = p.matcher(input); m.find(); ) {
    if (m.start(1) == -1) {
        System.out.println("Found Type I match for variable '" + m.group(2) + "'" +
                           " at index " + m.start() + "-" + m.end());
    } else {
        System.out.println("Found Type II match for variable '" + m.group(2) + "'" +
                           " at index " + m.start() + "-" + m.end());
    }
}

Output

Found Type II match for variable 'xxx' at index 8-15
Found Type I match for variable 'yyy' at index 27-33

UPDATE

If you want to replace the patterns with values, you can use appendReplacement() and appendTail().

Example:

String input = "adklsfjb$${xxx}dklsjfnsdklj${yyy}adljfhjh";

Map<String, String> type1 = new HashMap<>();
type1.put("xxx", "[type I with x's]");
type1.put("yyy", "[type I with y's]");

Map<String, String> type2 = new HashMap<>();
type2.put("xxx", "{TYPE 2 WITH x's}");
type2.put("yyy", "{TYPE 2 WITH y's}");

StringBuffer buf = new StringBuffer();
Matcher m = Pattern.compile("(\\$)?\\$\\{([^}]+)}").matcher(input);
while (m.find()) {
    String var = m.group(2);
    String repl = (m.start(1) == -1 ? type1.get(var) : type2.get(var));
    if (repl != null)
        m.appendReplacement(buf, Matcher.quoteReplacement(repl));
}
String output = m.appendTail(buf).toString();

System.out.println(output);

Output

adklsfjb{TYPE 2 WITH x's}dklsjfnsdklj[type I with y's]adljfhjh
Andreas
  • 154,647
  • 11
  • 152
  • 247
0

For variables of type ${varname} you can use this pattern:

(^|[^$])\$\{.*?\}

And for variables of type $${varname} you can use what you already had in mind:

\$\$\{.*?\\}

Sample Code:

String input = "${beef} is a great thing to $${eat}.  It has many ${health} benefits ";
       input + "and is low in fat ${too}";

// single dollar sign variables
System.out.println("single dollar sign variables:");
String pattern = "(?:^|[^$])(\\$\\{.*?\\})";
Pattern r = Pattern.compile(pattern);

Matcher m = r.matcher(input);
while (m.find()) {
    System.out.println("Found value: " + m.group(1));
}

// two dollar sign variables
System.out.println("two dollar sign variables:");
pattern = "(\\$\\$\\{.*?\\})";
r = Pattern.compile(pattern);

m = r.matcher(input);
while (m.find()) {
    System.out.println("Found value: " + m.group(1));
}

Output:

single dollar sign variables:
Found value: ${beef}
Found value: ${health}
Found value: ${too}
two dollar sign variables:
Found value: $${eat}

Demo here:

Rextester

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • I haven't tried your answer in Java code, however when I put on regexr.com, the string matched also included the prefix character. In your input string the prefix character are all white-spaces so it should not be a problem, however I'm also dealing with URLs so I probably gonna need to filter the result one more time. Anyway thanks for the suggestion :) – mrawesome Jun 08 '17 at 06:50
  • You entered the regex wrongly. Use the regex from my Java code: `(?:^|[^$])(\$\{.*?\\})` ... this should work correctly. – Tim Biegeleisen Jun 08 '17 at 06:57