正規表現の先読みと後読み
正規表現の先読みと後読みについて、
イマイチ認識がぼんやりしてたので少し詳しく整理してみた
コードはrubyで書いてます
-
先後読みの書式
- 先読み : (?=)
- 後読み : (?<=)
- 否定の先読み : (?!)
- 否定の後読み : (?<!)
特徴として、先後読みにマッチしたテキストはマッチから取り除かれる
-
先読みの動き
先読みはそれまでにマッチした位置を記憶して
先読み部分にマッチするかをチェックする先読み部分にマッチしたら
先読み部分を削除して記憶していた位置に戻るつまり、下記の例だと、
最初の文字列aaabbbはマッチするが、aaacccはマッチしないstr = "aaabbb aaaccc" str.scan(/aaa(?=bbb)/) => ["aaa"]
さらに、マッチ位置が先読み部分の先頭位置になるため、
先読みの後に以下のようにcccを足してみると何もマッチしない
これは先読みのマッチ後、aaaの最後のaの直後に
マッチしているので次の文字がbとなるため。str = "aaabbb aaaccc aaabbbccc" str.scan(/aaa(?=bbb)ccc/) => [] # 何もマッチしない str.scan(/aaa(?=bbb)\w+/) => ["aaabbb", "aaabbbccc"] # マッチ対象を単語構成文字とするとaaabbbとaaabbbcccがマッチ
-
後読みの動き
後読みは正規表現エンジンが到達した位置のすぐ左に
後読みに含まれているテキストが存在するかをチェックする
後読み部分にマッチしたら、後読みマッチ部分を削除する
下記の例だと、最初の文字列bbbaaaはマッチするが、cccaaaはマッチしないstr = "bbbaaa cccaaa" str.scan(/(?<=bbb)aaa/) => ["aaa"]
後読みの直前にcccを追加すると何もマッチしなくなる
これは、後読みが現在位置の左側をチェックしに行くため、
cccの部分を参照する事が原因(cccとbbbはどうやってもマッチしない)str = "bbbaaa cccaaa cccbbbaaa" str.scan(/ccc(?<=bbb)aaa/) => [] # 何もマッチしない str.scan(/\w+(?<=bbb)aaa/) => ["bbbaaa", "cccbbbaaa"] # マッチ対象を単語構成文字とするとbbbaaaaとcccbbbaaaがマッチ
-
HTMLタグの中身だけを取り出す正規表現
"normal bold normal"の様なHTMLテキストから
bタグの中身だけにマッチする正規表現を考えるstr = "normal <b>bold</b> normal" regex = /(?<=<b>)\w+(?=<\/b>)/ str.match(regex) => #<MatchData "bold">
開始タグを後読みで、終了タグを先読みでマッチさせるのがポイント
先読みは現在位置より右側をチェックして、
後読みは現在位置の左側をチェックする
そして、先後読みはグループのような記述だけど実際は
アンカーのような、位置にマッチするだけのものである
という理解が大事だと思う先後読みをマッチ結果から対象を除外する特殊なグループ、
のように考えてしまうとregex = /(?=<b>)\w+(?=<\/b>)/
こんな正規表現(前後両方を先読み)でも
タグの中身が取れてしまうような勘違いをしてしまうが
実際は開始タグの部分の先読みが完了した時点で<タグの直前に
マッチしている事になるので\wではなにもマッチしない事になるので注意。