替换字符串中的反向引用语法(为什么是美元符号?

2022-08-31 22:12:58

在 Java 中,似乎在其他一些语言中,模式中的反向引用前面有一个反斜杠(例如 , , , 等),但在替换字符串中,它们前面是一个美元符号(例如 , , , 和 )。\1\2\3$1$2$3$0

下面是一个用于说明的代码段:

System.out.println(
    "left-right".replaceAll("(.*)-(.*)", "\\2-\\1") // WRONG!!!
); // prints "2-1"

System.out.println(
    "left-right".replaceAll("(.*)-(.*)", "$2-$1")   // CORRECT!
); // prints "right-left"

System.out.println(
    "You want million dollar?!?".replaceAll("(\\w*) dollar", "US\\$ $1")
); // prints "You want US$ million?!?"

System.out.println(
    "You want million dollar?!?".replaceAll("(\\w*) dollar", "US$ \\1")
); // throws IllegalArgumentException: Illegal group reference

问题:

  • 在替换字符串中使用 for backreference 是 Java 独有的吗?如果不是,是什么语言启动了它?什么口味使用它,什么不使用它?$
  • 为什么这是一个好主意?为什么不坚持使用相同的模式语法?这难道不会带来一种更有凝聚力和更容易学习的语言吗?
    • 如果上面的语句 1 和 4 是“正确”的,而不是 2 和 3,语法不是会更简化吗?

答案 1

在替换字符串中使用 $ 作为 Java 独有的反向引用吗?

不。Perl使用它,Perl肯定早于Java的类。Java的正则表达式支持用Perl正则表达式明确描述。Pattern

例如:http://perldoc.perl.org/perlrequick.html#Search-and-replace

为什么这是一个好主意?

好吧,显然你不认为这是一个好主意!但是,这是一个好主意的一个原因是使Java搜索/替换支持(更多)与Perl兼容。

还有另一个可能的原因,为什么可能被视为比 更好的选择。也就是说,必须像在Java字符串文本中一样编写。$\\\\

但所有这些都是纯粹的猜测。当做出设计决定时,我们都没有在房间里。最终,他们为什么以这种方式设计替换字符串语法并不重要。这些决定已经做出并具体确定,任何进一步的讨论都纯粹是学术性的......除非你只是碰巧在为Java设计一种新的语言或一个新的正则表达式库。


答案 2

在做了一些研究之后,我现在已经理解了这些问题:Perl必须使用不同的符号进行模式反向引用和替换反向引用,虽然不必效仿,但它选择这样做,不是出于技术原因,而是出于传统原因。java.util.regex.*


在 Perl 方面

(请记住,我目前对Perl的所有了解都来自阅读维基百科文章,因此请随时纠正我可能犯的任何错误)

在Perl中必须以这种方式完成它的原因如下:

  • Perl 用作 sigil(即附加到变量名称的符号)。$
  • Perl 字符串文本是变量内插的。
  • Perl 正则表达式实际上将组捕获为变量 、等。$1$2

因此,由于Perl的解释方式及其正则表达式引擎的工作方式,必须在模式中使用反向引用(例如)的前斜杠,因为如果使用sigil代替(例如),它会导致意外的变量插值到模式中。\1$$1

替换字符串,由于它在Perl中的工作方式,在每个匹配的上下文中进行评估。Perl在这里使用变量插值是最自然的,因此正则表达式引擎将组捕获到变量,等等,以使它与语言的其余部分无缝地工作。$1$2

引用


在Java方面

Java是一种与Perl非常不同的语言,但最重要的是没有变量插值。此外,是一个方法调用,与 Java 中的所有方法调用一样,参数在调用方法之前计算一次。replaceAll

因此,变量插值功能本身是不够的,因为从本质上讲,必须在每次匹配时重新评估替换字符串,而这并不是Java中方法调用的语义。在调用之前计算的变量插值替换字符串实际上是无用的;插值需要在方法期间进行,在每场比赛上。replaceAll

由于这不是Java语言的语义,因此必须手动执行此“即时”插值。因此,绝对没有技术原因可以解释为什么在替换字符串中使用反向引用的转义符号。它很可能是.相反,模式中的反向引用也可以用 代替 来转义,并且从技术上讲它仍然同样有效。replaceAll$\$\

Java以它的方式进行正则表达式的原因纯粹是传统的:它只是遵循Perl设定的先例。