👁️

正規表現の先読みと後読みの使い方

2021/02/28に公開

はじめに

株式会社じげん の Web エンジニア、橋濱です!

正規表現にだんだん慣れてきたものの、含む・含まないでよく使う正規表現の「先読み」と「後読み」がいまいちで、いつもググりながら使ってます。
というわけで、もうググらなくても使えるようこの辺りを整理してみました。

どういう場合に使うか

先読み・後読みはどちらも、 特定の文字が前方(or 後方)に隣接している文字 をマッチさせるパターンです。

たとえば、文字列 hogefuga は、

  • fuga が前方(右側)に隣接している hoge であるし、
  • hoge が後方(左側)に隣接している fuga

ですね。
この前方か後方かという視点の違いで先読み・後読みという2種類のマッチパターンが用意されています。

先読み・後読みってどっちがどっち?

「先に読む、後に読む…って何それ?」という感じで、日本語訳をそのまま受け入れると個人的にめちゃくちゃ分かりづらいです。
「先」とか「後」とかいうのはもちろん隣接の前後関係を表しているのだと思いますが、なんかどちらで解釈しても成り立ちそうじゃないですか?

というわけで、元々の英語で理解を進めます。

  • 先読みは lookahead(前方を見る、先を見る)
  • 後読みは lookbehind(後方を見る、振り返る)

プログラミングは横書き文化なので、進行方向としては 左から右 をベースとしてイメージしてください。

  • look ahead は、「隣接する・しないを確認したい文字」を起点(マッチさせたい文字)から 前方(右側)に見る
  • look behind は、「隣接する・しないを確認したい文字」を起点(マッチさせたい文字)から 後方(左側)に見る

つまり、上で書いた例でいうと、文字列 hogefuga について

  • 「前方(右側)にある fuga が隣接する・しない」を見て hoge をマッチさせようとしているのが lookahead(先読み) であるし、
  • 「後方(左側)にある hoge が隣接する・しない」を見て fuga をマッチさせようとしているのが lookbehind(後読み)

ということになります。

実際の使い方

ここまでが理解できればあとは簡単です!
肯定・否定の違いは文字どおり、隣接する・しないの違いでしかありません。

以下4種類、それぞれの記述と結果を見ていきましょう。

  • 肯定先読み
  • 否定先読み
  • 肯定後読み
  • 否定後読み

先読み

先読みは、特定の文字が前方(右側)に隣接している文字をマッチさせるパターンでしたね。

次の3行のテキストを検索対象とします。

imghoge
imgfuga
imgpiyo

肯定先読み

(?=) という記法を使います。

たとえば「 hoge を前方(右側)に隣接している img をマッチさせたい」とします。
その場合、正規表現パターンは img(?=hoge) となります。

imghoge # この行の img とマッチします
imgfuga
imgpiyo

否定先読み

(?!) という記法です。
ビックリしてるわけじゃないので !? と間違えないよう気をつけてください!
いつも ? スタートです。

fuga を前方(右側)に隣接して いない img をマッチさせたい」とすると、正規表現パターンは img(?!fuga) となります。

imghoge # この行の img とマッチします
imgfuga
imgpiyo # この行の img とマッチします

後読み

後読みは、特定の文字が後方(左側)に隣接している文字をマッチさせるパターンでしたね。

次の3行のテキストを検索対象とします。

hogebtn
fugabtn
piyobtn

肯定後読み

(?<=) という記法です。
(?<=) とするか (?>=) かで迷うかもしれませんが、山括弧の向き的に、前者だと 後方(左側)を見てる感 がありますよね!

piyo を後方(左側)に隣接している btn をマッチさせたい」とすると、正規表現パターンは (?<=piyo)btn となります。

hogebtn
fugabtn
piyobtn # この行の btn とマッチします

否定後読み

(?<!) という記法です。

hoge を後方(左側)に隣接して いない btn をマッチさせたい」とすると、正規表現パターンは (?<!hoge)btn となります。

hogebtn
fugabtn # この行の btn とマッチします
piyobtn # この行の btn とマッチします

まとめ

ユースケースとしては、特定の属性を含む HTML 要素周りの一括置換とかで使えそうですね!

「先読み」「後読み」という用語が、「前読み」「後ろ読み」とかだったら分かりやすかったのかもしれない…。

Discussion