StackShelf
正規表現 第7回

先読み・後読み(応用)|正規表現で条件だけチェックする【入門】

正規表現の先読み (?= ) 、否定先読み (?! ) 、後読み (?<= ) 、否定後読み (?<! ) を具体例で解説。キャプチャせずに条件を付ける応用技法を学びます。

6分で読める
正規表現先読み後読みlookaheadregex応用

先読み・後読みとは何か

先読み(lookahead)後読み(lookbehind)は、マッチ結果に文字を含めず、その位置の前後にある文字列が条件を満たすかだけを調べる技法です。英語では lookaround とまとめて呼ばれることもあります。

たとえば「数字の直後に円記号がある3桁」だけを探したいが、マッチ結果には数字だけ欲しい——こういうときに、通常のキャプチャだけでは不便な場面で活きます。

肯定先読み (?=...)

(?=パターン) は、この位置の右側(先)にパターンが続くことを要求します。マッチ幅はゼロで、右側の文字は消費しません。

# 数字3桁のうち、直後に「円」があるもの(数字部分だけマッチさせたい場合の一例)
\d{3}(?=円)
# "1200円" の 120 の部分の手前位置で先読みが成功

# パスワードに英字と数字の両方(全体検証の一部として)
^(?=.*[a-zA-Z])(?=.*\d).{8,}$

複数の (?=...) を並べると、「すべての条件を満たす」AND のような検査ができます。パスワードポリシーの簡易チェックでよく見る形です。

否定先読み (?!...)

(?!パターン) は、右側にパターンが続かないことを要求します。

# "cat" だが "category" の cat ではない(後続が egory でない)
cat(?!egory)

# 拡張子 .tmp 以外のファイル名(簡易)
^[^.]+(?!\.tmp$)

「〜で終わらない」「〜の直後ではない」という除外条件を、全体を書き直さずに付けられます。

肯定後読み (?<=...) と否定後読み (?<!...)

後読みは左側(後ろ)の条件を見ます。JavaScript は ES2018 から後読みをサポートしています(ブラウザ・Nodeのバージョンに注意)。

# 円マークの直後の数字(円はマッチに含めないイメージ)
(?<=¥)\d+

# 否定後読み: 行頭が # でない(コメント行除外の一部)
(?<!^)#  ※ 実際のログ形式に合わせて調整が必要

可変長の後読みが使えない方言もあるため、ドキュメントを確認してください。

なぜキャプチャではなく先読みか

キャプチャグループでも「前後の条件」を表現できる場合がありますが、置換や抽出の対象に余計な部分が入ることがあります。先読み・後読みは条件だけを付け、メインのマッチ部分をシンプルに保てます。

また、複数条件を AND 的に重ねるパスワード検証では、肯定先読みの連鎖が読みやすいことがあります。

実務に近い例

価格リストから税込みだけ: 行によっては (?=.*税込) のような行全体への先読みと組み合わせます(データ形式次第)。

メールのドメイン部分だけ(簡易): 完全なメール検証は複雑なため、学習用に (?<=@)[\w.-]+ のように @ の後ろを後読みで区切る例があります。本番では専用バリデータ推奨です。

重複単語の検出: \b(\w+)\s+\1\b は後方参照の例ですが、先読みと組み合わせた除外パターンも設計できます。

つまずきポイント

対応していない環境—— 古いブラウザやツールでは後読みが使えません。代替としてキャプチャや2段階の処理を検討します。

可変長の制限—— 一部エンジンは後読みの長さに制限があります。

読みやすさ—— 先読みが3つ以上並ぶと初心者には難しいので、コメントやテスト、関数への分割を検討しましょう。

先読みを使うかどうかの判断基準

次のようなときは先読みを検討します。(1)マッチ結果に条件部分を含めたくない、(2)複数の独立した条件をANDで付けたい、(3)除外条件だけを追加したい。逆に、抽出した部分をそのまま使うなら通常のキャプチャの方が単純なことが多いです。

チームでパターンを共有するときは、先読みが3つ以上連なると読み手が苦労します。コメント、テストケース、または「正規表現+通常の if 文」への分割を検討し、半年後の自分が読めるかどうかを基準にしてください。

Python との対応(参考)

Python の re モジュールでも (?=...) (?!...) が使えます。後読みは (?<=...) (?<!...) です。JavaScript 入門の前に Python でテキスト処理を学んでいる方は、同じ記法で試せるので、regex101 で Python フレーバーを選んで比較するのも有効です。

先読みは応用技法ですが、パスワードポリシー1行で書ける便利さから、チュートリアルやサンプルコードに頻出します。読めないときは、(?=.*[A-Z]) を「どこかに大文字が1文字以上ある」と日本語に訳してから理解する練習をしてください。

先読みなしで書く代替

「英字と数字を含む8文字以上」を、先読みなしで書くと複数の iftest に分かれます。可読性と一行での表現力のトレードオフです。チームのコーディング規約で「正規表現は1行まで」などのルールがある場合は、先読みの多用を避ける判断もあります。

段階的な学習パス

先読みは必須ではありません。まずキャプチャとアンカーで十分な検証が書けるようになってから、パスワードルールのサンプルで (?=.*[0-9]) を1つずつ足してみてください。理解が追いつかないパターンは、コメント付きでチームに質問し、regex101 の説明機能と照らし合わせると学習効率が上がります。本番の認証機能では、正規表現に加えて漏洩パスワードリストのチェックなど、多層防御を検討するのが一般的です。

セキュリティメモ

先読みを含む複雑なパターンも、ユーザー入力をそのまま結合して実行すると ReDoS のリスクがあります。認証まわりでは正規表現に加え、レート制限やロックアウトなど、別レイヤーの対策をセットにしてください。次回は JavaScript の test() で、ここまでのパターンを実際に動かします。

まとめ

先読み (?= ) (?! ) は右側の条件、後読み (?<= ) (?<! ) は左側の条件を、マッチ文字列に含めずにチェックします。パスワードルールや除外条件で威力を発揮しますが、環境対応と可読性に注意してください。次回は JavaScript 固有の書き方と API に焦点を当てます。

まとめ

先読み・後読みはマッチに文字を含めず、位置の前後の条件だけを検査する応用技法です。?= は肯定、?! は否定の先読み、?<= ?<! は後読みです。パスワードの複数条件や除外に便利ですが、環境のサポートと可読性に注意し、複雑になりすぎたら通常のコードに分離しましょう。

次にやること

次は JavaScript の RegExp リテラル、test/match/replace、フラグ g i m u など、ブラウザと Node での具体的な使い方を学びます。

よくある質問

先読みは初心者に必要ですか?

必須ではありませんが、パスワード検証や「直後に円がある数字」などでよく登場します。基本(文字クラス・量詞・アンカー)の後に学ぶのがおすすめです。

JavaScriptで後読みは使えますか?

ES2018 以降の RegExp でサポートされています。古い環境では使えないため、対象バージョンを確認してください。

先読みとキャプチャの違いは?

先読みは幅ゼロで結果に含まれません。キャプチャはマッチした部分を記憶し、置換や参照に使います。

複数の (?= ) はANDですか?

同じ位置ですべて成功する必要があるため、AND的に働くことが多いです。ただしパターン全体の構造次第なのでテストが必要です。

否定先読み (?! ) の例は?

cat(?!egory) で category の cat を除外するなど、「この後に続かない」条件を付けられます。

次に読むべき記事

同カテゴリ「正規表現」の記事

学習ルート

体系的に学びたい方はこちらから。

インフラ・ドキュメントコース →

あわせて読みたい