[英]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.