1

我有这个代码:

$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);

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

4

1 回答 1

5

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

您不能随便修改现有的正则表达式并期望使其工作,尤其是对于递归正则表达式。看来您采用现有的括号匹配正则表达式并对其进行修改。您的正则表达式存在一些问题:

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

    这可以通过将此部分更改[^{}]为禁止{}inside来解决{@block}{@end}。由于递归,嵌套{@block}{@end}仍将匹配。

    请注意,这将完全不允许{}{@block}{@end}. 根据转义方案,可以修改正则表达式以允许这种情况。

    我还更改了[^{}]from *to的量词,因为当整个组的量词是+时,没有理由匹配空字符串。([^{}]+|(?R))*

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

    由于[^{}]+可以匹配的内容和递归正则表达式可以匹配的内容是互斥的1,因此正则表达式没有歧义,可以在不回溯的情况下进行匹配。我们可以通过使用所有格量词来告诉引擎不要回溯,这是正常的量词,+后面加上。

最终解决方案是:

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

演示

脚注

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

于 2013-03-23T09:18:35.663 回答