反向引用 续

对匹配失败的组的反向引用


上一篇介绍的反向引用适用于所有正则表达式,除了少数根本不支持反向引用的正则引擎。

完全不匹配的捕获组的反向引用与完全不参与匹配的捕获组的反向引用之间是有差异的。正则表达式(q?)b\1与b匹配。q?是可选的,不匹配任何内容,导致(q?)成功匹配但是并不捕获任何内容。b匹配b ,\1成功匹配该组捕获的所有内容。

在大多数正则中,正则表达式(q)?b\1不匹配b 。(q)根本无法匹配,因此该组根本无法捕获任何东西。因为整个组都是可选的,所以引擎的确匹配b 。现在,引擎到达\1 ,它引用了完全没有参加匹配尝试的组。这将导致反向引用完全不匹配。既然没有?将\1设为可选,则整体匹配尝试失败。

少数例外之一是JavaScript。根据ECMA官方标准,对非参与捕获组的反向引用必须成功地匹配任何内容,就像对不捕获任何内容的组的反向引用一样。换句话说,在JavaScript中,(q?)b\1(q)?b\1都匹配b 。XPath也可以使用这种方式工作。

对不存在的捕获组的反向引用


在大多数正则表达式中,对不存在的组,例如(one)\7)的反向引用是错误的。但是也有例外。当正则表达式中的捕获组少于反斜杠后的数字时,JavaScript会将\1\7视为八进制转义。\8和\9是错误,因为8和9不是有效的八进制数字。

Java将对不存在的组的反向引用视为对已存在但从未参与匹配的组的反向引用。它们不是错误,但是根本不会匹配任何内容。

.NET稍微复杂一些。.NET支持一位数字和两位数的反向引用以及两位数的八进制转义符,且不带前导零。后向引用优先级高于八进制转义。因此如果捕获组少于12个,则\12是正则表达式中的换行符(八进制12 =十进制10)。如果正则表达式中具有12个或更多捕获组,则\12是对第12个组的反向引用。.NET不支持一位数字八进制转义。因此,\7是少于7个捕获组的正则表达式中的错误。

正向引用


包括JGsoft,.NET,Java,Perl,PCRE,PHP,Delphi和Ruby在内的许多现代正则表达式都允许向前引用。它们允许我们使用对稍后出现在正则表达式中的组的反向引用。正向引用显然仅在它们位于重复组中时才有用。然后,在某些情况下,组已经匹配后,正则表达式引擎会评估反向引用。在尝试组之前,反向引用会失败,就像对失败组的反向引用一样。

如果支持正向引用,则正则表达式(\22two|(one))+匹配oneonetwo 。在字符串的开头,\2失败。尝试另一种方法,第二个捕获组匹配one,然后第一个捕获组匹配one。然后重复第一组。这次,\2匹配第二组捕获的one。two则匹配two 。通过第一组的两次重复,正则表达式匹配了整个目标字符串。

JavaScript不支持正向引用,但不会将它们视为错误。在JavaScript中,正向引用始终会找到长度为零的匹配项,就像对非参与组的反向引用在JavaScript中所做的一样。因为这不是特别有用,所以XRegExp对这种情况它们会报错。在std :: regex,Boost,Python,Tcl和VBScript正向引用是错误的。

嵌套引用


嵌套引用是对引用的捕获组内部的反向引用。像正向引用一样,嵌套引用仅在它们位于重复的组内时才有用,例如(\1two|(one))+。如果支持嵌套引用,则此正则表达式也匹配oneonetwo 。在字符串的开头,\1失败。尝试另一个选择项,第二个捕获组匹配one,然后第一个捕获组匹配one。然后重复第一组。此时,\1匹配到的one由所在第一组的最后一次迭代捕获。正则表达式引擎是不是重新进入第一组这并不重要。当该组在退出前,与该组匹配的文本将存储到反向引用中。two则匹配two。通过第一组的两次重复,正则表达式匹配了整个目标字符串。如果你在匹配后检索来自捕获组的文本,,当第二组捕获第一次出现one的字符串时,第一组存储onetwo 。

该JGsoft,.NET,Java的,Perl的,和VBScript的正则都支持嵌套引用。PCRE也可以,但存在回溯到使用嵌套反向引用捕获组的错误。PCRE 8.01并没有解决这些错误,而是通过强制捕获具有嵌套引用的组为atomic来解决它们。因此,在PCRE中,(\1two|(one))+与(?>(\1two|(one)))+相同。这会影响基于PCRE正则表达式引擎的语言,如语言PHP,Delphi,以及[R 。

JavaScript和Ruby不支持嵌套引用,但是将它们视为对非参与组的反向引用,而不是错误。在JavaScript中,这意味着它们始终匹配零长度的字符串,而在Ruby中,它们始终无法匹配。在std::regex,Boost,Python和Tcl中,嵌套引用是一个错误。    

查看笔记

扫码一下
查看教程更方便