我有这段代码:
$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
一切都没问题!但是如果我改变了一个字符串和regexp:
$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);
我没有得到任何输出。为什么?我期待相同的输出。
答案 0 :(得分:5)
问题是回溯限制已经用尽。您随时可以修改backtracking limit。 但是,对于我遇到的情况,重写正则表达式是更好的解决方案。
您无论如何都无法修改现有的正则表达式并期望使其正常工作,尤其是对于递归正则表达式。您似乎将现有的括号匹配正则表达式并进行修改。你的正则表达式有一些问题:
[^()]*
:没有理由在()
部分的文本中排除{@block}{@end}
。但更严重的问题是它匹配{}
。引擎将一直到最近的()
或字符串的末尾,无法匹配,然后回溯。这就是达到回溯限制的原因。
可以通过将此部分更改为[^{}]
以禁用{}
内的{@block}{@end}
来解决此问题。由于递归,嵌套的{@block}{@end}
仍将匹配。
请注意,这将完全禁止在{}
中将{@block}{@end}
指定为文字。可以修改正则表达式以允许这种情况,具体取决于转义方案。
我还将[^{}]
的量词从*
更改为+
,因为当整个群组([^{}]+|(?R))
的量词是时,没有理由匹配空字符串*
。
/\{@block\}((?:[^{}]+|(?R))*)\{@end\}/
上述修改后,第二个问题是输入字符串无效。量词的默认行为是将执行回溯,直到找到匹配或所有可能性都用完为止。因此,在这些情况下,您将达到回溯限制。
由于[^{}]+
可以匹配以及递归正则表达式可以匹配的是互斥 1 ,因此正则表达式不是模糊的,可以在没有回溯的情况下进行匹配。我们可以通过使用占有量词来告诉引擎不要回溯,这是正常量词,后面加+
。
最终的解决方案是:
/\{@block\}((?:[^{}]++|(?R))*+)\{@end\}/
1 :很明显,因为文本匹配[^{}]+
永远不会以{
开头,而与递归正则表达式匹配的文本必须以{
开头