代码之家  ›  专栏  ›  技术社区  ›  Alana Storm

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

  •  65
  • Alana Storm  · 技术社区  · 15 年前

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

    考虑以下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的不同?)

    4 回复  |  直到 6 年前
        1
  •  50
  •   Community Keith    6 年前

    perlrequick

    如果正则表达式中的分组是 嵌套的$1获取具有 最左边的左括号,$2 下一个左括号等。

    警告 :不包括非捕获组的左括号(?=)

    更新

    我不经常使用PCRE,因为我通常使用真实的东西;),但是 PCRE's docs 显示与Perl相同的内容:

    子模式

    2. 它将子模式设置为捕获子模式。这意味着,当整个模式匹配时,与子模式匹配的主题字符串部分将通过 ovector 论证 pcre_exec() . 左括号从左到右计数(从1开始),以获取捕获子模式的编号。

    例如,如果字符串“红色国王”与模式匹配

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

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

    如果PCRE偏离了PerlRegex的兼容性,可能应该重新定义缩写词——“Perl同源正则表达式”、“PerlComparable正则表达式”或其他什么。或者只是剥离意义的字母。

        2
  •  16
  •   tchrist    11 年前

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

    • 爪哇 - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
      捕获组是通过从左到右计数其左括号来编号的。……“Group Zero始终代表整个表达式。”
    • .NET - http://msdn.microsoft.com/en-us/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
      要补充艾伦M所说的内容,请搜索“pcre_exec()如何返回捕获的子字符串”,并阅读以下第五段:

      The  first  pair  of  integers, ovector[0] and ovector[1], identify the
      portion of the subject string matched by the entire pattern.  The next
      pair  is  used for the first capturing subpattern, and so on. The value
      returned by pcre_exec() is one more than the highest numbered pair that
      has  been  set.  For example, if two substrings have been captured, the
      returned value is 3. If there are no capturing subpatterns, the  return
      value from a successful match is 1, indicating that just the first pair
      of offsets has been set.
      
    • Perl与众不同 - http://perldoc.perl.org/perlre.html#Capture-buffers
      $1、$2等。按照您的预期匹配捕获组(例如,通过出现左括号),但是$0返回程序名,而不是整个查询字符串-以获得使用$&的结果。

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

    你说首先列出内部捕获组同样合乎逻辑,你是对的——这只是一个在关闭而不是打开时索引的问题,parens。(如果我理解正确的话)。但这样做不太自然(例如,它不遵循阅读方向惯例),因此通过观察确定哪个捕获组将处于给定的结果索引更困难(可能不太明显)。

    将整个匹配字符串放在0位置也是有意义的-主要是为了一致性。它允许整个匹配的字符串保持在相同的索引中,而不管从ReGEX到ReGEX的数量捕获组,而不管实际上匹配任何对象的捕获组的数目(例如Java将折叠匹配组数组的长度,因为每个捕获组不匹配任何内容(例如,SOM)。就像“A(.*)模式”)。您可以随时检查捕获“group”结果[捕获“group”结果“length-2”,但这并不能很好地将语言转换为Perl,后者动态创建变量($1、$2等)(当然,Perl是一个坏例子,因为它对匹配的表达式使用了$&但是您得到了这个想法:)。

        3
  •  8
  •   Community Keith    7 年前

    我知道每个regex风格的数字都是按照左括号出现的顺序分组的。外部集团在其包含的子集团之前被编号只是一个自然结果,而不是明确的政策。

    有趣的是 命名组 . 在大多数情况下,它们遵循相同的策略,按照paren的相对位置进行编号——名称只是编号的别名。但是,在.NET正则表达式中,命名组与编号组分开编号。例如:

    Regex.Replace(@"one two three four", 
                  @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)",
                  @"$1 $2 $3 $4")
    
    // result: "two four one three"
    

    实际上, 是的别名 名称 ;分配给命名组的编号从“实际”编号组离开的位置开始。这似乎是一个奇怪的策略,但有一个很好的理由:在.NET正则表达式中,可以在正则表达式中多次使用同一组名。这使得正则表达式像来自 this thread 对于匹配来自不同地区的浮点数字:

    ^[+-]?[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})?
    )$
    

    如果有一个千位分隔符,那么无论regex的哪个部分匹配它,它都将保存在组“千”中。同样,十进制分隔符(如果有)也将始终保存在“十进制”组中。当然,在没有可重用命名组的情况下,有一些方法可以识别和提取分隔符,但是这种方法非常方便,我认为这不仅仅是证明奇怪的编号方案是正确的。

    还有Perl5.10+,它给了我们对捕获组的更多控制,而我不知道该怎么做。D

        4
  •  4
  •   Devin Ceartas    11 年前

    在我工作过的所有平台中,按照左paren的顺序捕获是标准的。(Perl、PHP、Ruby、Egrep)