正規表現テスト:ヒット・グループ・置換プレビューとゼロ幅の罠

正規表現テスターには三つの役割があります。一致位置とグループを明示し、置換結果を先に見せ、ゼロ幅一致で止まらないこと。

自分で作る理由

オンラインの正規表現テスターは数多くありますが、多くは検証したいテキストを他人のサーバーへ貼ることを求めてきます。そして「なぜここが強調されているのか」を説明してくれるものはほとんどありません。欲しかったのは、一致の内訳をそのまま並べるローカルのページでした。

結果が示すべき三つ

第一に範囲。各一致の開始と終了のオフセットです。オフセットが分からなければ、強調を正しい位置に描けません。

第二にグループ。全体の一致だけでなく、各キャプチャグループの内容と番号を並べます。参加しなかったグループは空文字ではなく undefined と表示します。後続の処理ではこの二つがまったく違うからです。

第三に置換プレビュー。一致箇所に色を付けるだけでなく、置換後の全文を出します。置換で最も多い誤りはグループ参照の間違いで、それは結果を見て初めて分かります。

ゼロ幅一致で止めない

実装で唯一の本物の罠です。g フラグ付きで全体一致を取るとき、一致長が 0 の場合(^ や $、純粋な先読みが該当します)、lastIndex が進まずループが終わりません。ページは固まります。

そこで一致のたびに位置を明示的に進めます。

const step = match[0].length === 0 ? 1 : 0;
re.lastIndex = match.index + match[0].length + step;

あわせて一致数の上限も設けます。壊滅的なバックトラックでブラウザーを巻き込まないためです。正規表現の書き間違いがページの停止と等価であってはなりません。

強調は文字列置換ではなく断片化で

元のテキストを「非一致 / 一致 / 非一致……」の断片配列に切り、テンプレートは断片を描画するだけにします。HTML にマークアップを挿し込まないので、利用者のテキストに含まれる記号で構造が壊れることはありません。

テンプレートとローカル実行

メール、URL、IPv4、日付などのパターンはテンプレートとして内蔵し、空欄から書き始めずに済むようにしています。解析はすべてブラウザ内で完結し、ロジックは依存も DOM も持たない純粋関数なので、プロジェクトの自検スクリプトが直接実行できます。上記のゼロ幅と上限の分岐もそこで覆っています。

← 記事一覧に戻る

コメント

…