正規表現のゼロ幅一致を安全にテストする方法
先読み、行頭・行末、単語境界など、文字を消費しない正規表現の一致位置と繰り返し処理を理解します。
公開日: 2026-08-03 · 更新日: 2026-08-03
ゼロ幅一致とは
正規表現には、条件には一致しても文字自体を消費しないパターンがあります。代表例は行頭の ^、行末の $、単語境界の \b、先読みの (?=...) と (?!...) です。このような一致では、開始位置と終了位置が同じになります。
たとえば (?=b) を ab に対して実行すると、b そのものではなく、b の直前にある位置へ一致します。一致文字列は空ですが、一致が存在しないわけではありません。
なぜ表示や繰り返しが難しいのか
通常の一致は文字列の範囲をハイライトできます。ゼロ幅一致には幅がないため、位置をカーソルやマーカーとして表示する必要があります。空文字列だけを出力すると、利用者には何も起きていないように見えます。
JavaScriptで g または y フラグを使う正規表現は、次の検索開始位置を lastIndex に保持します。ゼロ幅一致の後は終了位置が進まない場合があるため、exec() を単純なループで繰り返す実装では同じ位置に留まり続ける危険があります。
テストするときの確認項目
- パターンとフラグを分けて確認します。
- 一致文字列だけでなく、開始位置と終了位置を確認します。
- 実際に使うコードが
gまたはyフラグを使うか確認します。App Museumのテスターは一致一覧を表示するため全体を走査するので、本番コードで使うメソッドの挙動も別に確認します。 - 文字列の先頭、途中、末尾で一致する例を用意します。
- 空文字列に対する挙動も確認します。
次の例では、それぞれ異なる位置に一致します。
pattern: ^ input: ab position: 0
pattern: (?=b) input: ab position: 1
pattern: $ input: ab position: 2
正規表現テスターでは、空の一致結果だけでなく位置情報を確認してください。置換に使う場合は、その位置へ文字列を挿入する動作になることがあります。
実装側で無限ループを避ける
exec() を繰り返すコードでは、前回と同じ位置で空文字列に一致したことを検出し、Unicodeの扱いにも注意しながら次の文字位置へ進める必要があります。単純に1コード単位を加算すると、サロゲートペアの途中へ移動する可能性があります。
可能なら matchAll() など、用途に合う標準メソッドを検討してください。ただし、利用するメソッドによってグローバルフラグの要否や lastIndex の扱いが異なるため、対象ブラウザの仕様を確認します。
ゼロ幅一致が役立つ場面
- 区切り文字を結果へ含めず、その直前や直後を探す
- 行頭や行末へ文字列を挿入する
- 単語の境界だけを条件にする
- 後続文字を消費せずに条件を確認する
ゼロ幅一致は異常な結果ではなく、「文字列ではなく位置を選ぶ」正規表現として捉えると理解しやすくなります。