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