嵌套捕获组在正则表达式中如何编号?


84

对于正则表达式应如何处理嵌套括号的捕获行为,是否存在已定义的行为?更具体地说,您是否可以合理地期望不同的引擎将在第一个位置捕获外部括号,并在随后的位置捕获嵌套的括号?

考虑以下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,$ 3等访问该组。如何进入第十组?是10美元吗?我认为$ 10不会起作用,因为它将被解释为$ 1后跟0。这是否意味着我们最多只能有9个组?如果作者可以将其作为问题的一部分,那么这将是唯一了解正则表达式中嵌套组的地方。
LionHeart 2010年

Answers:


59

perlrequick

如果正则表达式中的分组是嵌套的,则$ 1将获得最左括号开头的组,$ 2将得到下一个括号开头的组,依此类推。

注意:不包括非捕获组的开头括号(?=)

更新资料

我使用PCRE的程度不高,因为通常使用的是真实的;),但是PCRE的文档显示的内容与Perl相同:

子公司

2.它将子模式设置为捕获子模式。这意味着,当整个模式匹配时,与子模式匹配的主题字符串部分将通过的ovector参数传递回调用方pcre_exec()。从左到右(从1开始)对左括号进行计数,以获取捕获子图案的编号。

例如,如果字符串“ the red king”与该模式匹配

the ((red|white) (king|queen))

捕获的子字符串为“红色国王”,“红色”和“国王”,并分别编号为1、2和3。

如果PCRE偏离了Perl regex兼容性,则可能应该重新定义首字母缩写-“ Perl关联正则表达式”,“ Perl可比正则表达式”或其他名称。或者只是剥离含义的字母。


1
@Sinan:他正在PHP中使用PCRE,这是“ Perl兼容的正则表达式”;因此应该与直接使用Perl完全相同
Pascal MARTIN

3
Pascal,PCRE最初是作为Perl兼容正则表达式集的尝试,但近年来两者之间略有不同。仍然非常相似,但是高级功能集之间存在细微的差异。(此外,根据每个问题,我对所有平台都感兴趣)
Alan Storm,

1
实际上,如今,Perl正在执行大部分“漂流”工作,但是您是对的:“与Perl兼容”正在迅速从错误的名称变为不合理的名称。:D
艾伦·摩尔

1
@ Alan,Perl肯定在前进。P5.10更改了一些内容,但6将会有很大的不同。P几乎肯定需要被解释为“ Perl 5”。PCRE是一个伟大的项目,我对此赞不绝口,它在多个项目中都是天赐之物。
daotoad

1
我在第一个引言Caveat下添加了此内容:不包括非捕获组的开头括号(?=)。我没有意识到编辑时没有登录。只有当我添加此评论时,才会提示我输入凭据。因此,现在还需要1个人批准!
JGFMK

17

是的,对于您感兴趣的所有语言,这些定义都很好:

  • Java - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    “捕获组是通过从左到右计算其开括号来编号的。...组零总是代表整个表达式。”
  • .Net - http://msdn.microsoft.com/zh-cn/library/bs2twtah
    (VS.71) .aspx “使用()的捕获是根据开头括号的顺序自动编号的,从一个开始。捕获,捕获元素编号为零,是与整个正则表达式模式匹配的文本。”)
  • PHP(PCRE功能) - http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    “\ 0或$ 0是指整个模式匹配的文本。左括号从左到右(从1开始)进行计数,以获取捕获子模式的数量。” (不推荐使用的POSIX函数也是如此)
  • PCRE - http
    ://www.pcre.org/pcre.txt要添加Alan M的内容,搜索“ pcre_exec()如何返回捕获的子字符串”,然后阅读下面的第五段:

    第一对整数ovector [0]和ovector [1]标识
    主题字符串的一部分与整个模式匹配。下一个
    对用于第一个捕获子模式,依此类推。价值
    pcre_exec()返回的数字比最大的数字对多一
    已经设置好了。例如,如果捕获了两个子字符串,则
    返回值为3。如果没有捕获子模式,则返回
    成功匹配的值是1,表示只有第一对
    偏移量已设置。
    
  • Perl的不同- http://perldoc.perl.org/perlre.html#Capture-buffers
    $ 1,$ 2等匹配捕获组,如你所期望(即通过打开支架的出现),但是$ 0返回程序的名称,而不是整个查询字符串-使用$&代替。

对于其他语言(Python,Ruby和其他语言),您很有可能会找到类似的结果。

您说先列出内部捕获组同样是合乎逻辑的,并且您是对的-这只是在关闭而不是打开parens上建立索引的问题。(如果我对您的理解正确)。这样做虽然不那么自然(例如,它不遵循阅读方向惯例),所以使通过检举确定哪个捕获组处于给定的结果索引变得更加困难(可能不太明显)。

将整个匹配字符串置于位置0也很有意义-主要是为了保持一致性。不管从正则表达式到正则表达式的捕获组数目如何,以及实际上与任何内容都匹配的捕获组数目如何,它都允许整个匹配的字符串保持相同的索引(例如,对于每次捕获,Java都会折叠匹配组数组的长度组不匹配任何内容(例如,像“ a(。*)pattern”之类的东西)。您可以随时检查capture_group_results [capturing_group_results_length-2],但是这种语言无法很好地翻译成Perl,可以动态创建变量($ 1 ,$ 2等。)(当然,Perl是一个糟糕的例子,因为它对匹配的表达式使用$&,但您会明白:)。


1
很好的答案。但是也要为Python(2&3)更新

那JavaScript呢?!!
mesqueeb

9

我知道每种正则表达式都按开头括号出现的顺序对组进行编号。在外围小组之前对外部小组进行编号只是一个自然的结果,而不是明确的政策。

有趣的是命名组。在大多数情况下,它们遵循相同的按括号相对位置进行编号的策略-名称仅仅是数字的别名。但是,在.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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.