では、爆発的バックトラッキング(catastrophic backtracking)を起こす典型パターンと、それを 安全に書き換えた例 を一覧表にしました。Python 初心者〜中級者が理解しやすいように、短いコード例付きにします。
爆発的バックトラッキング パターン一覧と安全な書き換え例
| # | 危険なパターン | 何が危険か | サンプル文字列 | 安全な書き換え例 | 解説 |
|---|---|---|---|---|---|
| 1 | (a+)+$ | 貪欲量指定の多重ネスト → 失敗時に組み合わせを全探索 | "aaaaab" | a+$ | 余計な括弧を省き、1回の貪欲量指定に変更 |
| 2 | `(a | aa)+$` | 選択肢の共通部分で分岐爆発 | "aaaaab" | a+$ |
| 3 | `(a | aa | aaa)+$` | 選択肢が多い場合の指数探索 | "aaaaab" |
| 4 | (.*a)+b | .* が貪欲 → バックトラック爆発 | "aaaaac" | .*?a または文字クラス限定 | 非貪欲 *? で最小マッチにする、または文字クラスで範囲を限定 |
| 5 | (a+)?a{30}b | オプションと量指定の組み合わせで分岐が多すぎる | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" | a{31}b | 明確に回数を指定し、分岐を減らす |
| 6 | `(a | b | ab)+c` | 共通接頭辞によるバックトラック | "ababababx" |
| 7 | (\d+)*$ | 数字の多重ネスト | "1234567x" | \d+$ | 余計なグループの重複を削除 |
| 8 | `(ab | a)*c` | 選択肢の共通部分 → 失敗時に組み合わせ全探索 | "ababababx" | `(?:ab |
| 9 | ([a-z]+)+d | 小文字文字列の多重ネスト | "aaaaaaaax" | [a-z]+d | 1回の貪欲量指定で十分 |
| 10 | (a.*b)+c | 貪欲な .* + 繰り返し → 長い文字列で爆発 | "axxxxxxxbxxxxxxc" | (a.*?b)+c | .*? 非貪欲で試行回数を減らす |
補足ポイント
- 貪欲量指定
*/+の多重ネスト は最も典型的な爆発パターン。 - 選択パターン OR (
|) の共通接頭辞 はバックトラックを増やす原因。 - 解決策の共通原則
- 余計な括弧
()を非キャプチャ(?: )にする - 貪欲
*→ 非貪欲*?を使う - OR の共通部分は文字クラス
[ ]でまとめる - 回数が分かる場合は
{m,n}で明示 - 可能なら正規表現を分割して処理する
- 余計な括弧
Python で安全性を確認する方法
import re, time
patterns = [r"(a+)+$", r"(a|aa)+$"]
text = "a" * 30 + "b"
for pat in patterns:
start = time.time()
re.match(pat, text)
end = time.time()
print(f"{pat}: {end - start:.5f}秒")
Python- 危険パターンは文字数が増えると秒数が爆発的に伸びます。
- 安全パターンに置き換えると瞬時に終わります。
💡 この表を使えば、「この書き方は危険 → 安全な書き方に置き換え」 の基本ルールがすぐに確認できます。
実務で長いテキストやログを扱う場合、まずこの表のパターンをチェックするだけで安全性がぐっと上がります。
