簡體   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