If performance is not a priority, you can use the appendReplacement
method of the Matcher
class:
public class StrSubstitutor {
private Map<String, String> map;
private static final Pattern p = Pattern.compile("\$\{(.+?)\}");
public StrSubstitutor(Map<String, String> map) {
this.map = map;
}
public String replace(String str) {
Matcher m = p.matcher(str);
StringBuilder sb = new StringBuilder();
while (m.find()) {
String var = m.group(1);
String replacement = map.get(var);
m.appendReplacement(sb, replacement);
}
m.appendTail(sb);
return sb.toString();
}
}
A more performant but uglier version, just for fun :)
public String replace(String str) {
StringBuilder sb = new StringBuilder();
char[] strArray = str.toCharArray();
int i = 0;
while (i < strArray.length - 1) {
if (strArray[i] == '$' && strArray[i + 1] == '{') {
i = i + 2;
int begin = i;
while (strArray[i] != '}') ++i;
sb.append(map.get(str.substring(begin, i++)));
} else {
sb.append(strArray[i]);
++i;
}
}
if (i < strArray.length) sb.append(strArray[i]);
return sb.toString();
}
It's about 2x as fast as the regex version and 3x faster than the apache commons version as per my tests. So the normal regex stuff is actually more optimized than the apache version. Usually not worth it of course. Just for fun though, let me know if you can make it more optimized.
Edit: As @kmek points out, there is a caveat. Apache version will resolve transitively. e.g, If ${animal}
maps to ${dog}
and dog
maps to Golden Retriever
, apache version will map ${animal}
to Golden Retriever. As I said, you should use libraries as far as possible. The above solution is only to be used if you have a special constraint which does not allow you to use a library.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…