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}部分を除外する理由がない。しかし、より深刻な問題は、それが一致すること{}です。エンジンは最も近い()文字列または文字列の最後まで検索し、一致しない場合はバックトラックします。これが、バックトラッキングの制限に達した理由です。

    これは、この部分を に変更して内部[^{}]を禁止することで修正できます。再帰により、ネストされたものは引き続き一致します。{}{@block}{@end}{@block}{@end}

    {}これにより、 内でテキストとして指定することは完全に禁止されることに注意してください{@block}{@end}。エスケープ方式によっては、このようなケースを許可するように正規表現を変更できる場合があります。

    また、グループ全体の量指定子が である場合に空の文字列に一致する理由がないため、の量指定子を[^{}]から*に変更します。+([^{}]+|(?R))*

    /\{@block\}((?:[^{}]+|(?R))*)\{@end\}/
    
  • 上記の変更後の 2 番目の問題は、無効な入力文字列です。quantifier のデフォルトの動作は、一致が見つかるかすべての可能性が尽きるまでバックトラックが実行されることです。したがって、これらの場合、バックトラッキングの制限に達します。

    一致できるもの[^{}]+と再帰正規表現が一致できるものは相互に排他的であるため1、正規表現はあいまいではなく、後戻りせずに一致できます。通常の量指定子に後ろを追加して、所有量指定子を使用することで、バックトラックしないようにエンジンに指示できます。+

最終的な解決策は次のとおりです。

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

デモ

脚注

1 :再帰正規表現に一致するテキストは で始まらなければならないのに対し、テキストマッチング[^{}]+は決して で始まらないため、これは明らかです。{{

于 2013-03-23T09:18:35.663 に答える