Ruby の正規表現の先読みと後読み、全然理解できません。
挙動は理解できます。単語の意味が分からないのです。全くピンと来ない。
仕方ないので、できる限り他の日本語に変換して噛み砕いてみようというのがこの記事の趣旨です。多分この手の記事は既出だと思うので、すでに仕様を理解されている方、自分なりに結論が出ている方には必要ないかもしれません。単に単語が分かりにくいだけなので。
TL;DR
- 先読み … ある位置から先の文字列があるパターンにマッチするか
- 後読み … ある位置より前の文字列があるパターンにマッチするか
言い換えると、先読み → 前方確認、後読み → 後方確認していると考えると少しは理解しやすいかもしれません(少なくとも自分は理解しやすいです)。が、しかしこの解釈の仕方は自分にとって重要です。
何が問題点か
そもそも読み方は、「先読み(さきよみ)」「後読み(あとよみ)」だと思うんですが、これを初見で聞いて何の話なのかピンと来ませんでした。
- 「先読み(さきよみ)」 … 先を読む?先に読む?
- 「後読み(あとよみ)」 … 後ろを読む?前を読む?後で読む?
先(さき)と後(あと)という読み方だけを見ると、「評価するタイミングのことを言っているのかな?」と思ってしまいます。ですが、ここで言っているのは「ある位置から先か後ろか」ということで、時間の話ではないようです。
時間の話か、位置の話か、これがはっきりしない(ように見える)ことが問題のように感じます。
Ruby の公式ドキュメントを確認する
ある位置から続く文字列が ある部分式にマッチするならばその位置にマッチする という正規表現を書くことができます。「ある位置から続く文字列(先読み、lookahead)/ある位置の手前までの文字列(後読み、lookbehind)」 と「マッチする(肯定、positive)/マッチしない(否定、negative)」 の組み合わせで4つのパターンがあります。
https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html
パターン | 名称(日本語) | 名称(英語) |
---|---|---|
(?=pat) | 肯定先読み | positive lookahead |
(?!pat) | 否定先読み | negative lookahead |
(?<=pat) | 肯定後読み | positive lookbehind |
(?<!pat) | 否定後読み | negative lookbehind |
ドキュメントを読むと、英語の表現も含めて位置関係を示していることが分かると思います。
「ある位置から続く文字列」なのか
「ある位置の手前までの文字列」なのか
/(?<=<code>).+?(?=<\/code>)/
↑の例の場合、<code></code>に挟まれている文字列にマッチします(結構パフォーマンス悪そう…)。<code>から続くかつ</code>よりも手前の文字列を見ているっちゅーことですね。時間とかタイミングとか全く関係ないじゃん(いや評価するタイミングとか内部的には色々あるかもしれんけれども)。
先読み/後読みは前方確認/後方確認と変換する
まとめると、先(さき)とか後(あと)とか言っていますが、文字列の位置関係の話なので、なにか空間的な何かを感じる単語の方がしっくり来るのではないかと思います(諸説あり)。
もし、「なんだかな〜しっくりこねえなあ〜」と阿藤快状態になっている方いましたらご参考にしていただければ幸いです。
ちなみに「こんどこそわかる(肯|否)定(先|後)読み」という記事はとても参考になったので、ぜひ合わせて読んでみていただければと思います。