カクカクしかじか

技術的なアレコレ

正規表現 ~肯定的先(後)読み / 否定的先(後)読み~

はじめに

正規表現を書かないといけないことは頻繁にないものの、そのせいかいつまで経っても自分の中に定着せずに毎回調べているので、観念してちょっとずつまとめようかと思い投稿します。

正規表現の検証に便利なサイト

正規表現のチェックを行うのに便利なサイトを念のためこちらへ。

Rubyの場合

rubular.com

JavaScriptの場合

scriptular.com

今回扱う題材

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

肯定的(否定的)先読み(後読み)ってナニコレ?

特定のキーワードの前後のパターンマッチを計りたい時に用いられる正規表現の種類のことらしいです。 以下にざっくりそれぞれの示す内容をまとめてみました。

肯定的先読み

例えば、ファミリーネームが「レノン」でラストネームが「ジョン」であるジョン・レノンという名前のユーザーを探したい場合は、レノンという文字列の前にジョンという文字列があることをチェックする必要があります。

肯定的先読みのパターン例

肯定的先読みの基本パターンは (?=期待するパターン) です。以下の例にある .? は 「.」任意の1文字が「?」あってもなくても良いという意味で、 ジョンレノン でも ジョン・レノン でもマッチするようにしています。

ジョン(?=.?レノン)

image.png

Rubyのコードで確認するとこのパターンでマッチすることを確認しました。

> "ジョン・レノン"  =~ /ジョン(?=.*?レノン)/
=> 0
> "ジョンレノン"  =~ /ジョン(?=.*?レノン)/
=> 0

肯定的後読み

肯定的先読みの反対で逆に今度は、レノンの前がジョンであるユーザーを探します。

肯定的後読みのパターン例

(?<=期待するパターン)

image.png

こちらでもRubyのコードで確認するとこのパターンでマッチすることを確認しました。

> "ジョンレノン"  =~ /(?<=ジョン).?レノン/
=> 3
> "ジョン・レノン"  =~ /(?<=ジョン).?レノン/
=> 3

否定的先読み

ジョン・レモンのようなジョンは合っているが、レノンが間違っているパターンをマッチさせます。

否定的先読みのパターン例

ジョン(?!.?レノン)

image.png

念のためRubyのコードでも確認してみると。

> "ジョン・レモン"  =~ /ジョン(?!.?レノン)/
=> 0

否定的後読み

否定的先読みの逆で、ジャン・レノンのようなレノンは合っているが、ジョンが合っていないパターンをマッチさせます。

否定後読みのパターン例

(?<!ジョン).?レノン

image.png

念のためRubyのコードでもマッチを確認してOKでした。

> "ジャン・レノン"  =~ /(?<!ジョン).?レノン/
=> 3

まとめ

とにかく、先読みと後読みがややこしい...
先頭が決まっていてその後ろでパターンの一致を判定するのが 先読み で、後方が決まっていてその前のパターン一致を判定するのが 後読み という理解で良いのでしょうか?
つまり、先読みでは後ろに!後読みでは前に!肯定か否定したいパターンを書くと良いということにしておきます。

# 先読み
ジョン(?=.?レノン)

# 後読み
(?<=ジョン).?レノン

最後に

以下の4つのパターンを表にまとめてみたので、困った時はこれを都度チェックすることにしました。
雑ですが...とりあえず今回はここまで!

  • 肯定先読み
  • 否定先読み
  • 肯定後読み
  • 否定後読み
書き方 意味
(?=regex) 直後にregexある
(?!regex) 直後にregex無い
(?<=regex) 直前にregexある
(?<!regex) 直前にregex無い