3

Input String

${abc.xzy}/demo/${ttt.bbb}
test${kkk.mmm}

RESULT World/demo/Hello testSystem

The text inside the curly brackets are keys to my properties. I want to replace those properties with run time values.

I can do the following to get the regex match but what should i put in the replace logic to change the ${..} matched with the respective run time value in the input string.

Pattern p = Pattern.compile("\\{([^}]*)\\}");
Matcher m = p.matcher(s);
while (m.find()) {
  // replace logic comes here
}
1

4 Answers 4

3

You may use the following solution:

String s = "${abc.xzy}/demo/${ttt.bbb}\ntest${kkk.mmm}";
Map<String, String> map = new HashMap<String, String>();
map.put("abc.xzy", "World");
map.put("ttt.bbb", "Hello");
map.put("kkk.mmm", "System");
StringBuffer result = new StringBuffer();
Matcher m = Pattern.compile("\\$\\{([^{}]+)\\}").matcher(s);
while (m.find()) {
    String value = map.get(m.group(1));
    m.appendReplacement(result, value != null ? value : m.group());
}
m.appendTail(result);
System.out.println(result.toString());

See the Java demo online, output:

World/demo/Hello
testSystem

The regex is

\$\{([^{}]+)\}

See the regex demo. It matches a ${ string, then captures any 1+ chars other than { and } into Group 1 and then matches }. If Group 1 value is present in the Map as a key, the replacement is the key value, else, the matched text is pasted back where it was in the input string.

Sign up to request clarification or add additional context in comments.

1 Comment

This is better than my solution. Didn't know about appendReplacement. Quite neat feature.
2

An alternative may be using a third-party lib such as Apache Commons Text. They have StringSubstitutor class looks very promising.

Map valuesMap = HashMap();
valuesMap.put("abc.xzy", "World");
valuesMap.put("ttt.bbb", "Hello");
valuesMap.put("kkk.mmm", "System");

String templateString = "${abc.xzy}/demo/${ttt.bbb} test${kkk.mmm}"
StringSubstitutor sub = new StringSubstitutor(valuesMap);
String resolvedString = sub.replace(templateString);

For more info check out Javadoc https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/StringSubstitutor.html

Comments

0

Your regex needs to include the dollar. Also making the inner group lazy is sufficient to not include any } in the resulting key String.

String regex = "\\$\\{(.+?)\\}";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
while (m.find()) {
    String key = m.group(1); // This is your matching group (the one in braces).
    String value = someMap.get(key);    
    s.replaceFirst(regex, value != null ? value : "missingKey");

    m = p.matcher(s); // you could alternatively reset the existing Matcher, but just create a new one, for simplicity's sake.
}

You could streamline this, by extracting the cursor position, and doing the replacement yourself, for the string. But either way, you need to reset your matcher, because otherwise it will parse on the old String.

Comments

0

The_Cute_Hedgehog's answer is good, but includes a dependency.
Wiktor Stribiżew's answer is missing a special case.

My answer aim to using java build-in regex and try to improve from Wiktor Stribiżew's answer. (Improve in Java code only, the regex is Ok)

Improvements:

  • Using StringBuilder is faster than StringBuffer
  • Initial StringBuilder capable to (int)(s.length()*1.2), avoid relocating memory many times in case of large input template s.
  • Avoid the case of regex special characters make wrong result by appendReplacement (like "cost: $100"). You can fix this problem in Wiktor Stribiżew's code by escape $ character in the replacement String like this value.replaceAll("\\$", "\\\\\\$")

Here is the improved code:

        String s = "khj${abc.xzy}/demo/${ttt.bbb}\ntest${kkk.mmm}{kkk.missing}string";
        Map<String, String> map = new HashMap<>();
        map.put("abc.xzy", "World");
        map.put("ttt.bbb", "cost: $100");
        map.put("kkk.mmm", "System");
        StringBuilder result = new StringBuilder((int)(s.length()*1.2));
        Matcher m = Pattern.compile("\\$\\{([^}]+)\\}").matcher(s);
        int nonCaptureIndex = 0;
        while (m.find()) {
            String value = map.get(m.group(1));
            if (value != null) {
                int index = m.start();
                if (index > nonCaptureIndex) {
                    result.append(s.substring(nonCaptureIndex, index));
                }
                result.append(value);
                nonCaptureIndex = m.end();
            }
        }
        result.append(s.substring(nonCaptureIndex, s.length()));
        System.out.println(result.toString());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.