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

时间:2010-05-23 04:56:09

标签: java regex syntax replace backreference

在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

问题:

  • 是否使用$作为Java特有的替换字符串的反向引用?如果没有,用什么语言开始呢?什么口味使用它,什么不使用?
  • 为什么这是个好主意?为什么不坚持使用相同的模式语法?这不会导致更具凝聚力和更容易学习的语言吗?
    • 如果上面的语句1和4是“正确的”而不是2和3,语法是否会更加简化?

2 个答案:

答案 0 :(得分:33)

  

在Java中使用$替换字符串中的反向引用是什么?

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

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

  

为什么这是一个好主意?

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

还有另一个可能的原因$可能被视为比\更好的选择。那就是\必须在Java String文字中写成\\

但所有这些都是纯粹的猜测。在作出设计决定时,我们没有人在房间里。最终,为什么他们以这种方式设计替换String语法并不重要。这些决定已经制定并具体设置,任何进一步的讨论都纯粹是学术性的......除非您恰好为Java设计新语言或新的正则表达式库。

答案 1 :(得分:18)

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


在Perl方面

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

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

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

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

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

参考


在Java端

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

因此,变量插值特征本身是不够的,因为实质上必须在每次匹配时重新评估替换字符串,而这不是Java中方法调用的语义。在replaceAll被调用之前评估的变量插值替换字符串实际上是无用的;插值需要在方法期间,每次匹配时发生

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

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