问题在于您重新开始扫描以进行下一次替换,并且您首先扫描最短的输入。
通过在abc 之前扫描a,您将永远不会匹配abc,因为在扫描abc 之前,a 已被其他内容替换。按长度对要扫描的部分进行排序,最长在前,如commented by rgettman。
通过对每个输入重新开始扫描,您可以替换替换值的内容。例如。如果您在a 之前扫描b,b 会被false 替换,但随后false 中的a 会被true 替换,从而导致b 开始被ftruelse 替换.
要解决第二个问题并提高性能,您应该只扫描一次输入。
扫描一次输入(扫描多个文本之一)的最简单方法是使用像abc|a|b 这样的正则表达式,即列出以| 分隔的键,最长的键在前。
假设替换映射可以是任何东西,您应该从 Map 动态构建正则表达式,记住在映射中引用键和值,因此任何特殊字符都不会被视为正则表达式特殊字符,例如匹配任意字符.。
这是使用正则表达式替换循环完成所有这些操作的方法(在 Java 8 中):
public static String replace(String input, Map<String, String> replacementValues) {
String regex = replacementValues.keySet().stream()
.sorted(Comparator.comparingInt(String::length).reversed()
.thenComparing(Function.identity()))
.map(Pattern::quote)
.collect(Collectors.joining("|"));
StringBuffer buf = new StringBuffer(input.length() + 16);
Matcher m = Pattern.compile(regex).matcher(input);
while (m.find())
m.appendReplacement(buf, Matcher.quoteReplacement(replacementValues.get(m.group())));
return m.appendTail(buf).toString();
}
测试
Map<String, String> map = new HashMap<>();
map.put("a", "true");
map.put("b", "false");
map.put("abc", "true");
System.out.println(replace("abc|a&b", map));
输出
true|true&false
如果扫描应该匹配单词而不是子字符串,请将正则表达式编译为:
Pattern.compile("\\b(?:" + regex + ")\\b")