ど素人から毛を生やす。<延>

長大な文字列でpreg_match()したらマッチするはずなのにマッチしない場合、backtrack_limitに抵触している可能性がある話。

Web > PHP 2021年7月8日(最終更新:28日前)

どもです。

件名の通り、数MBの文字列をpreg_match()するというトンデモ行為をせにゃならず。
その結果、マッチするはずなのに「0」をreturnされてしまいました。

この原因が「バックトラックリミット」でした。という話です。

バックトラックリミットとは?

正規表現は、前から順に適用されていくのですが、後続のパターンがマッチしない場合に一つ前のパターンに戻って、別のマッチ方法を試行するのをバックトラック(backtracking)と呼びます。

Shin x Blog

原理や詳細は参考元サイトを見ていただくとして、要点は

「*」や「+」を大量の文字列に対して使用すると、1回のマッチでものすごい数の「バックトラック」が発生して「バックトラックリミット」に到達。
その場合は「マッチしなかった」という結果になる。

というものです。

解決法は?

・長い文字列のマッチを行うなら「*」や「+」を使わない。怠けずに文字数を指定する。
・そもそもバックトラックリミットに抵触しかねない長文をマッチしようとしない。

これが最善。しかしそれができたら苦労は要らない場合もあります。
そういったときは、バックトラックの上限値を引き上げます。

ini_set('pcre.backtrack_limit', 10000000); //初期値は大体1000000

もっと詳しく知りたい場合

現象と確認方法、対応策について
みどりのウェブ開発日記[PHP の正規表現で preg_match がおかしい時は pcre.backtrack_limit の上限を増やしてみる]

原理について
Qiita[正規表現のパフォーマンスの話をされても全くピンと来なかった僕は、backtrackに出会いました。]

この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)