替换字符串中的反向引用语法(为啥是美元符号?)
Posted
技术标签:
【中文标题】替换字符串中的反向引用语法(为啥是美元符号?)【英文标题】:Backreferences Syntax in Replacement Strings (Why Dollar Sign?)替换字符串中的反向引用语法(为什么是美元符号?) 【发布时间】:2011-02-22 20:36:18 【问题描述】:在 Java 中,似乎在其他一些语言中,模式中的反向引用前面有一个反斜杠(例如 \1
、\2
、\3
等),但在替换字符串中它们前面有一个美元符号(例如$1
、$2
、$3
和 $0
)。
这里有一个 sn-p 来说明:
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,那么语法会不会更精简?
【问题讨论】:
\1
和 \2
是八进制转义序列,分别用八进制数字 1 和 2 描述字符(参见 java.sun.com/docs/books/jls/second_edition/html/…)。这就是为什么您需要不同的引用语法。
另见***.com/questions/3556558/…
【参考方案1】:
在替换字符串中使用 $ 进行反向引用是 Java 独有的吗?
没有。 Perl 使用它,而且 Perl 肯定早于 Java 的 Pattern
类。 Java 的正则表达式支持是根据 Perl 正则表达式明确描述的。
例如:http://perldoc.perl.org/perlrequick.html#Search-and-replace
为什么这是个好主意?
显然你认为这不是一个好主意!但这是一个好主意的一个原因是使 Java 搜索/替换支持(更多)与 Perl 兼容。
$
可能被视为比\
更好的选择还有另一个可能原因。也就是说,\
必须在 Java 字符串文字中写为 \\
。
但这一切纯属猜测。做出设计决定时,我们都没有在房间里。最终,他们为什么以这种方式设计替换 String 语法并不重要。决策已经做出并具体化,任何进一步的讨论都纯属学术性的......除非您恰好正在为 Java 设计一种新语言或新的正则表达式库。
【讨论】:
+1 同意...现在很多正则表达式引擎都按照他们的方式做事,因为 Perl 就是这样做的。所以要真正理解它,你必须理解 Perl 背后的原因。 (警告:不要在家里尝试) Perl pwns 在正则表达式。你现在到处都能看到它:javascript、XML、Java、php 等等。 "Perl pwns at regex" - 有人愿意帮我把它翻译成英文吗?【参考方案2】:在做了一些研究之后,我现在明白了这些问题:Perl 不得不为模式反向引用和替换反向引用使用不同的符号,而 java.util.regex.*
没有有 效仿,它选择效仿,不是出于技术原因,而是出于传统原因。
在 Perl 方面
(请记住,我目前对 Perl 的所有了解都来自阅读 Wikipedia 文章,因此请随时纠正我可能犯的任何错误)
不得不在 Perl 中这样做的原因如下:
Perl 使用$
作为标记(即附加到变量名的符号)。
Perl 字符串文字是变量插值。
Perl 正则表达式实际上将组捕获为变量$1
、$2
等。
因此,由于 Perl 的解释方式及其正则表达式引擎的工作方式,必须在模式中使用前面的斜杠来表示反向引用(例如 \1
),因为如果使用符号 $
代替(例如 @ 987654329@),它会导致意外的变量插值到模式中。
替换字符串,由于它在 Perl 中的工作方式,在每个匹配的上下文中进行评估。 Perl 在这里使用变量插值是最自然的,因此正则表达式引擎将组捕获到变量 $1
、$2
等中,以使其与语言的其余部分无缝工作。
参考文献
Wikipedia/String literal - variable interpolation Wikipedia/Sigil (computer programming)在 Java 方面
Java 是一种与 Perl 非常不同的语言,但最重要的是这里没有变量插值。此外,replaceAll
是一个方法调用,与 Java 中的所有方法调用一样,参数在调用方法之前被评估一次。
因此,仅靠变量插值功能是不够的,因为本质上必须在每次匹配时重新评估替换字符串,而这不是 Java 中方法调用的语义。在replaceAll
被调用之前评估的变量插值替换字符串实际上是没有用的;插值需要发生在方法中,在每场比赛中。
由于这不是 Java 语言的语义,replaceAll
必须手动执行此“即时”插值。因此,绝对没有技术原因为什么$
是替换字符串中反向引用的转义符号。很可能是\
。相反,模式中的反向引用也可以使用$
而不是\
进行转义,并且在技术上仍然可以正常工作。
Java 使用正则表达式的原因纯粹是传统的:它只是遵循 Perl 设置的先例。
【讨论】:
在正则表达式中$
已经被用作锚;使用它作为反向引用的标志会非常混乱,如果不是不可能的话。在替换字符串中,反斜杠用于消歧;如果$10
可以指第十组,但您希望它表示第一组后跟零,则改写$1\0
。当然,您可以使用它来转义文字 $
。这与它在正则表达式和 Java 字符串文字中的使用是一致的。所以这不是一个完全武断的选择。以上是关于替换字符串中的反向引用语法(为啥是美元符号?)的主要内容,如果未能解决你的问题,请参考以下文章