replacestring中的反向引用语法(为什么是美元符号?)
在Java中,似乎在其他一些语言中,模式中的反向引用前面有一个反斜杠(例如\1
, \2
, \3
等),但是在replacestring中,它们前面是美元符号(例如$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中使用独特的replacestring反向引用
$
? 如果不是,什么语言开始呢? 什么味道使用它,什么不使用? - 为什么这是一个好主意? 为什么不坚持相同的模式语法? 这不会导致更有凝聚力和更容易学习的语言吗?
- 如果上面的语句1和4是“正确的”而不是2和3,语法是不是会更简化?
在Java中使用独特的replacestring反向引用$?
不,Perl使用它,而且Perl早于Java的Pattern
类。 Java的正则expression式支持是用Perl正则expression式来明确描述的。
例如: http : //perldoc.perl.org/perlrequick.html#Search-and-replace
为什么这是一个好主意?
显然,你不认为这是一个好主意! 但是,一个好主意的原因是让Javasearch/replace支持(更多)与Perl兼容。
还有另一个可能的原因,为什么$
可能被视为比\
更好的select。 那就是\
必须写成\\
在Java String文字中。
但所有这些都是纯粹的猜测。 在做出devise决定时,我们没有人在场。 最终,他们为什么以这种方式devise替代的String语法并不重要。 这些决定已经被制定和具体化,任何进一步的讨论纯粹是学术的,除非你碰巧正在为Javadevise一种新的语言或新的正则expression式库。
在做了一些研究之后,我已经了解了这些问题:Perl 必须使用不同的符号来进行模式反向引用和replace反向引用,而java.util.regex.*
则不必遵循,它会select一个技术性而非传统的理由。
在Perl方面
(请记住,现在我所知道的关于Perl的知识都来自于维基百科的文章,所以请随时纠正我可能犯的错误)
在Perl中必须这样做的原因如下:
- Perl使用
$
作为签名(即附加到variables名称的符号)。 - Perlstring文字是可变插值的。
- Perl的正则expression式实际上捕获组variables
$1
,$2
等
因此,由于Perl被解释的方式以及它的正则expression式引擎是如何工作的,所以必须使用前面的反向引用斜线(例如\1
),因为如果使用sigil $
(例如$1
),会导致意外可变插值到模式中。
replacestring,由于它在Perl中的工作原理,在每个匹配的上下文中进行评估。 Perl在这里使用可变插值是最自然的,因此正则expression式引擎将组捕获到variables$1
, $2
等中,以使其与其余语言无缝地工作。
参考
- 维基百科/string文字 – 可变插值
- 维基百科/ Sigil(电脑编程)
在Java方面
Java是一种与Perl不同的语言,但最重要的是没有可变插值。 而且, replaceAll
是一个方法调用,与Java中的所有方法调用一样,在调用方法之前,参数被评估一次。
因此,variables插值function本身是不够的,因为实质上replacestring必须在每次匹配时重新计算,这不仅仅是Java中的方法调用的语义。 在replaceAll
被调用之前求值的variables插值replacestring实际上是无用的; 在每个匹配的方法中都需要进行插值。
由于这不是Java语言的语义,因此replaceAll
必须手动执行此“即时”插值。 因此, 绝对没有技术原因,为什么$
是replacestring反向引用的逃避符号。 这可能是很好的\
。 相反,模式中的反向引用也可以用$
而不是\
来逃脱,并且它在技术上仍然可以工作。
Java所采用的方式是纯粹的传统:它只是遵循Perl设定的先例。