繁体   English   中英

关于php正则表达式的递归模式

[英]About php regexp for recursive pattern

我有以下代码:

$string="some text {@block}outside{@block}inside{@}outside{@} other text";

function catchPattern($string,$layer){
  preg_match_all(
    "/\{@block\}".
      "(".
        "(".
           "[^()]*|(?R)".
        ")*".
      ")".
    "\{@\}/",$string,$nodes);
  if(count($nodes)>1){
    for($i=0;$i<count($nodes[1]); $i++){
      if(is_string($nodes[1][$i])){
        if(strlen($nodes[1][$i])>0){
          echo "<pre>Layer ".$layer.":   ".$nodes[1][$i]."</pre><br />";
          catchPattern($nodes[1][$i],$layer+1);
        }
      }
    }
  }
}

catchPattern($string,0);

这给了我这个输出:

Layer 0:   outside{@block}inside{@}outside

Layer 1:   inside

没关系! 但是,如果我更改了一个字符串和正则表达式:

$string="some text {@block}outside{@block}inside{@end}outside{@end} other text";

function catchPattern($string,$layer){
  preg_match_all(
    "/\{@block\}".
      "(".
        "(".
           "[^()]*|(?R)".
        ")*".
      ")".
    "\{@end\}/",$string,$nodes);
  if(count($nodes)>1){
    for($i=0;$i<count($nodes[1]); $i++){
      if(is_string($nodes[1][$i])){
        if(strlen($nodes[1][$i])>0){
          echo "<pre>Layer ".$layer.":   ".$nodes[1][$i]."</pre><br />";
          catchPattern($nodes[1][$i],$layer+1);
        }
      }
    }
  }
}

catchPattern($string,0);

我没有得到任何输出。 为什么? 我期望相同的输出。

问题是回溯限制已用尽。 您可以随时修改回溯限制 但是,对于我遇到的情况,重写正则表达式是更好的解决方案

您不能以任何方式修改现有的正则表达式并期望使其正常工作,特别是对于递归正则表达式。 似乎您采用了与括号匹配的正则表达式并对其进行了修改。 正则表达式中存在一些问题:

  • [^()]* :没有理由在{@block}{@end}部分的文本中排除() 但更严重的问题是它与{}匹配。 引擎将一直到最接近的()或字符串的末尾,不匹配,然后回溯。 这就是达到回溯限制的原因。

    这可以通过改变该部分以被固定[^{}]为不允许{}{@block}{@end} 由于递归,嵌套的{@block}{@end}仍将匹配。

    请注意,这将完全禁止将{}指定为{@block}{@end}文本 根据转义方案,可以修改正则表达式以允许这种情况。

    我还改变的量词[^{}]*+ ,因为没有理由在整个组的量词来匹配一个空字符串([^{}]+|(?R))*

     /\\{@block\\}((?:[^{}]+|(?R))*)\\{@end\\}/ 
  • 经过上述修改后,第二个问题是输入字符串无效。 量词的默认行为是执行回溯,直到找到匹配项或所有可能性用尽。 因此,在这种情况下,您将达到回溯限制。

    由于[^{}]+可以匹配的内容与递归正则表达式可以匹配的内容是互斥的1 ,因此该正则表达式不是模棱两可的,可以进行匹配而无需回溯。 我们可以通过使用所有格量词 (通常的量词,后跟+告诉引擎不要回溯。

最终的解决方案是:

/\{@block\}((?:[^{}]++|(?R))*+)\{@end\}/

演示版

脚注

1 :很明显,因为匹配[^{}]+的文本永远不会以{开头,而匹配递归正则表达式的文本必须以{开头。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM