使用 java matcher.replaceAll() / appendReplacement() 的“安全方式”

Posted

技术标签:

【中文标题】使用 java matcher.replaceAll() / appendReplacement() 的“安全方式”【英文标题】:"Safe way" to use java matcher.replaceAll() / appendReplacement() 【发布时间】:2020-09-14 06:04:36 【问题描述】:

大部分情况下我们用正则表达式替换字符串段,当替换的文本是一个变量,所以基本上程序员是不知道的。

但是我们总是忘记,java matcher.replaceAll() 的行为将非常依赖于替换本身。因此,替换不应包含任何 '$' 或 '\' 字符,以提供 naive 结果。

例如以下代码抛出“java.lang.IndexOutOfBoundsException: No group 2”,以防变量薪水等于“$2”。

String salary = "$2";
Pattern p = Pattern.compile("SAL");
Matcher m = p.matcher("Salary: SAL");
String s = m.replaceAll(salary);
System.out.println(s);

我知道,如果“$”符号用“\”转义,那么我们将得到预期的结果。但话又说回来,'\' 也应该用 '\' 转义。所以正确的解决方案是:

String salary = "$2";
Pattern p = Pattern.compile("SAL");
Matcher m = p.matcher("Salary: SAL");
String s = m.replaceAll(salary.replace("\\", "\\\\").replace("$", "\\$"));
System.out.println(s);

首先,这使用起来不太方便,而且在性能方面也不是很好。 (对于 appendReplacement() 方法也是如此。)

那么你能推荐一些更通用的解决方案吗?

【问题讨论】:

'有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。' 您所说的“性能不太好”是什么意思?你的意思是你不喜欢两个replace 调用必须循环遍历字符串两次?编写自己的escapeReplacement 方法,使用StringBuilder 并且只循环字符串一次怎么样? 性能方面:我通过两个方法调用引入了转义字符,然后 Matcher 将搜索我的转义符以将它们替换回来。 不太好:正则表达式已经很昂贵了,所以这个额外的钩子可能不会影响整体性能。 【参考方案1】:

如果您只想用指定的文字替换序列替换特定的子字符串,您可以简单地使用String.replace()。像这样:

  String source = "Salary: SAL";
  String target = "SAL";
  String salary = "$2";
  String result = source.replace(target, salary);
  System.out.println(result); // prints "Salary: $2"

值得注意的是,它只替换文字子字符串序列,如果target 是一个正则表达式,它将不起作用。

【讨论】:

你完全正确。这个例子是不对的。请假设,模式应该类似于 "S\\w*" @BalazsKelemen 正如我所料。无论如何都要把它留在这里,以防万一它对某人有用。遗憾的是,我不知道有什么更好的方法来处理正则表达式。

以上是关于使用 java matcher.replaceAll() / appendReplacement() 的“安全方式”的主要内容,如果未能解决你的问题,请参考以下文章

使用Java代码和注解完成Spring配置

我应该采用 Java 12 还是坚持使用 Java 11?

java语言Class类的作用是啥以及怎么使用?

怎么使用JAVA中的包

什么是java控件?怎样使用java控件?

使用java语言,如何对一个类中的静态方法做切面编程?