問題
PHP はPCRE正規表現ライブラリを使用しますが、これは後読みでの繰り返しをサポートしていません。
繰り返しが後読み (例: (?<=\d+)
) にある場合、PHP は通常、次のような警告を発行します。
警告: preg_match_all(): コンパイルに失敗しました: 10 行目の lookbehind.php のオフセット 7 で、後読みアサーションが固定長ではありません
しかし、コンパイルが失敗するはずなのに失敗しないケースを発見しました。
予想どおり、これらはコンパイルに失敗します。
/(?<=X*)a/
/(?<=X+)a/
/(?<=(X)*)a/
ただし、/(?<=(X)+)a/
コンパイルします。/(?<=(X){1,})a/
これは、同様にコンパイルされる と機能的に同等である必要があります。一方、実際にその範囲に上限を追加すると
(例: /(?<=(X){1,2})a/
)、コンパイルに失敗します。私もコンパイルに失敗するはずだ/(?<=(X)+)a/
と思いますが、そうではありません。/(?<=(X){1,})a/
なぜだめですか?
実験
ここにいくつかのコードがあります:
$str = 'aXaaXXaaaXXXaaaa';
$regex = '/(?<=((?:X)+))a+/';
preg_match_all($regex, $str, $matches, PREG_OFFSET_CAPTURE|PREG_SET_ORDER);
print_r($matches);
複数の の周りにキャプチャ グループを追加するために、パターンを少し複雑にしましたX
。ここに私の結果があります:
Array (
[0] => Array (
[0] => Array (
[0] => aa
[1] => 2
)
[1] => Array (
[0] => X
[1] => 1
)
)
[1] => Array (
[0] => Array (
[0] => aaa
[1] => 6
)
[1] => Array (
[0] => X
[1] => 5
)
)
[2] => Array (
[0] => Array (
[0] => aaaa
[1] => 12
)
[1] => Array (
[0] => X
[1] => 11
)
)
)
a
s に続くsと明確に一致しX
ます。これは正しいです。X
ただし、サブパターン 1は、すべてではなく1 つのみに一致するように見えます。a
後読みの先頭にan を追加してX
、間にあるすべての s を検出する必要がある場合、結果は次のようになります。
$regex = '/(?<=(a(?:X)+))a+/';
Array (
[0] => Array (
[0] => Array (
[0] => aa
[1] => 2
)
[1] => Array (
[0] => aX
[1] => 0
)
)
)
1 回だけ一致します ( が 1 つしかない場合X
)。事実上、(X)+
に(X){1,}
縮小されて(X){1}
います (これは固定長のため許容されます)。
結論
「バグ!」と泣くのは嫌いです。期待どおりに動作しないものを見つけたらすぐに、しかしそれは確かにそのように思えます。パターンは期待どおりに拒否されず、有効なパターンであっても期待どおりに動作しません。
だから私は尋ねます:
- このように動作する正当な理由はありますか?
- これが に当てはまるのに に当てはまらないのはなぜ
+
ですか*
? - 括弧が重要な理由:
X+
失敗します。(X)+
許可されています ?
どんな洞察も大歓迎です。ありがとうございました。