[英]How are nested capturing groups numbered in regular expressions?
对于正则表达式应如何处理嵌套括号的捕获行为,是否存在已定义的行为? 更具体地说,您是否可以合理地期望不同的引擎将在第一个位置捕获外部括号,并在随后的位置捕获嵌套的括号?
考虑以下PHP代码(使用PCRE正则表达式)
<?php
$test_string = 'I want to test sub patterns';
preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
print_r($matches);
?>
Array
(
[0] => I want to test sub patterns //entire pattern
[1] => I want to test //entire outer parenthesis
[2] => want //first inner
[3] => to //second inner
[4] => patterns //next parentheses set
)
首先捕获整个带括号的表达式(我要测试),然后捕获内部的带括号的模式(“ want”和“ to”)。 这在逻辑上是合理的,但我可以看到首先要捕获子括号,然后再捕获整个模式的逻辑情况也是如此。
因此,这是“捕获全部事物优先”在正则表达式引擎中定义的行为,还是取决于模式的上下文和/或引擎的行为(PCRE与C#不同,而Java与Java不同)比等)?
如果正则表达式中的分组是嵌套的,则$ 1将获得最左括号开头的组,$ 2将得到下一个括号开头的组,依此类推。
注意 :不包括非捕获组的开头括号(?=)
更新资料
我使用PCRE的程度不高,因为通常使用的是真实的;),但是PCRE的文档显示的内容与Perl相同:
子公司
2.
将子模式设置为捕获子模式。 这意味着,当整个模式匹配时,与子模式匹配的主题字符串部分将通过pcre_exec()
的ovector
参数传递回调用方。 从左到右(从1开始)对左括号进行计数,以获取捕获子图案的编号。例如,如果字符串“ the red king”与该模式匹配
the ((red|white) (king|queen))
捕获的子字符串为“红色国王”,“红色”和“国王”,并分别编号为1、2和3。
如果PCRE偏离了Perl regex兼容性,则可能应该重新定义首字母缩写-“ Perl关联正则表达式”,“ Perl可比正则表达式”或其他名称。 或者只是剥离含义的字母。
是的,对于您感兴趣的所有语言,这一切都定义得很好:
PCRE - http: //www.pcre.org/pcre.txt
要添加Alan M所说的内容,请搜索“ pcre_exec()如何返回捕获的子字符串”,然后阅读下面的第五段:
\n 第一对整数ovector [0]和ovector [1]标识\n 主题字符串的一部分与整个模式匹配。 下一个\n 对用于第一个捕获子模式,依此类推。 价值\n pcre_exec()返回的值比编号最高的对多一\n 已经设置好了。 例如,如果捕获了两个子字符串,则\n 返回值为3。如果没有捕获子模式,则返回\n 成功匹配的值是1,表示只有第一对\n 偏移量已设置。\n
对于其他语言(Python,Ruby和其他语言),您很有可能会找到类似的结果。
您说先列出内部捕获组同样是合乎逻辑的,并且您是对的-这只是在关闭而不是打开parens上建立索引的问题。 (如果我对您的理解正确)。 这样做虽然不那么自然(例如,它不遵循阅读方向惯例),所以使通过检举确定哪个捕获组处于给定的结果索引变得更加困难(可能不太明显)。
将整个匹配字符串置于位置0也很有意义-主要是为了保持一致性。 它允许整个匹配的字符串保持相同的索引,而不考虑从正则表达式到正则表达式的捕获组的数目,以及实际上与任何内容都匹配的捕获组的数目(例如,对于每次捕获,Java都会折叠匹配组数组的长度组不匹配任何内容(例如,像“ a(。*)pattern”之类的东西)。您可以随时检查capture_group_results [capturing_group_results_length-2],但是这种语言不能很好地翻译成Perl,可以动态创建变量($ 1 ,$ 2等。)(当然,Perl是一个不好的例子,因为它对匹配的表达式使用$&,但是您会发现这个主意:)。
我知道每种正则表达式都按开头括号出现的顺序对组进行编号。 在外围小组之前对外部小组进行编号只是一个自然的结果,而不是明确的政策。
有趣的是命名组 。 在大多数情况下,它们遵循相同的编号规则,即通过括号的相对位置进行命名-名称只是数字的别名。 但是,在.NET正则表达式中,命名组是与编号组分开编号的。 例如:
Regex.Replace(@"one two three four",
@"(?<one>\w+) (\w+) (?<three>\w+) (\w+)",
@"$1 $2 $3 $4")
// result: "two four one three"
实际上, 数字是名称的别名; 分配给命名组的编号从“真实”编号组开始的地方开始。 这看起来似乎是一个奇怪的策略,但是有一个充分的理由:在.NET正则表达式中,您可以在同一个正则表达式中多次使用相同的组名。 这使得可能使用该线程中的正则表达式来匹配来自不同语言环境的浮点数:
^[+-]?[0-9]{1,3}
(?:
(?:(?<thousand>\,)[0-9]{3})*
(?:(?<decimal>\.)[0-9]{2})?
|
(?:(?<thousand>\.)[0-9]{3})*
(?:(?<decimal>\,)[0-9]{2})?
|
[0-9]*
(?:(?<decimal>[\.\,])[0-9]{2})?
)$
如果有一个千位分隔符,则无论正则表达式的哪一部分匹配,它都将保存在“千”组中。 同样,十进制分隔符(如果有的话)将始终保存在“十进制”组中。 当然,有一些方法可以识别和提取没有可重用的命名组的分隔符,但是这种方法更加方便,我认为这比证明怪异的编号方案更为合理。
然后是Perl 5.10+,它使我们对捕获组的控制比我所知道的要多。 :D
在我使用过的所有平台上,按左括号顺序进行捕获的顺序都是标准的。(perl,php,ruby,egrep)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.