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

pol*_*nts 47 java regex syntax backreference replace

在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
Run Code Online (Sandbox Code Playgroud)

问题:

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

Ste*_*n C 34

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

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

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

为什么这是个好主意?

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

还有另一个可能的原因$可能被视为一个更好的选择\.那就是\必须像\\Java String文字一样编写.

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

  • +1同意......现在很多正则表达式引擎都按照他们的方式做事,因为Perl这样做了.所以要真正理解它,你必须要理解Perl背后的原因.(警告:不要在家里尝试) (9认同)
  • Perl在正则表达式中表现出色.你现在到处都看到它:JavaScript,XML,Java,PHP等等. (5认同)
  • *"Perl pwns at regex"* - 是否有人愿意为我翻译成英文? (2认同)

pol*_*nts 20

之后做一些研究,我现在已经明白的问题: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设置的先例.

  • 在正则表达式中,`$`已被用作锚点; 使用它作为反向引用的印记,即使不是不可能,也会非常混乱.在替换字符串中,反斜杠用于消除歧义; 如果`$ 10`可以引用第十组但你想要它意味着第一组后跟零,你会写'$ 1\0`而不是.当然,你用它来逃避文字`$`.这与它在正则表达式和Java字符串文字中的使用是一致的.所以这不是一个完全武断的选择. (4认同)