在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特有的替换字符串的反向引用?如果没有,用什么语言开始呢?什么口味使用它,什么不使用?答案 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中以这种方式完成的原因如下:
$
作为sigil(即附加到变量名称的符号)。$1
,$2
等。因此,由于Perl的解释方式及其正则表达式引擎的工作方式,必须使用模式中的反向引用前导斜杠(例如\1
),因为如果使用了sigil $
相反(例如$1
),它会导致非预期的变量插值到模式中。
替换字符串由于在Perl中的工作方式,在每次匹配的上下文中进行评估。 Perl最自然地在这里使用变量插值,因此正则表达式引擎将组捕获到变量$1
,$2
等中,以使其与语言的其余部分无缝协作。
Java是一种与Perl截然不同的语言,但最重要的是,这里没有变量插值。此外,replaceAll
是一个方法调用,与Java中的所有方法调用一样,参数在调用方法之前被计算一次。
因此,变量插值特征本身是不够的,因为实质上必须在每次匹配时重新评估替换字符串,而这不是Java中方法调用的语义。在replaceAll
被调用之前评估的变量插值替换字符串实际上是无用的;插值需要在方法期间,每次匹配时发生。
由于这不是Java语言的语义,replaceAll
必须手动执行“即时”插值 。因此,绝对没有技术原因为什么$
是替换字符串中反向引用的转义符号。它很可能是\
。相反,模式中的反向引用也可以使用$
而不是\
进行转义,并且它在技术上仍然可以正常工作。
Java以正确的方式进行正则表达式的原因纯粹是传统的:它只是遵循Perl设置的先例。