実に2週間ぶりの更新。転職してそろそろ2ヶ月になる頃だけど、実案件で苦戦しまくっている。今回の話もそこから出てきたネタ。
正規表現には先読みアサーション 後読みアサーション なるものがあることを知ったので、今回はそれについて書く。
公式ドキュメントはこちら
6.2. re — 正規表現操作 — Python 3.6.3 ドキュメント
正規表現 HOWTO — Python 3.6.3 ドキュメント
先読み
先読みアサーション
(?=...)
... が続く文字列にマッチする。例えば以下のようになる。
>>> import re >>> m = re.search(r'Nikon(?=FE2)', 'NikonFE2') >>> m.group(0) 'Nikon'
マッチしないものも混ぜてみる。
>>> import re
>>> cameras = ['NikonFE', 'NikonFE2', 'NikonFM2']
>>> for camera in cameras:
... m = re.search(r'Nikon(?=FE2)', camera)
... if m is not None:
... print(m.group(0), camera)
... else:
... print('None', camera)
...
None NikonFE
Nikon NikonFE2
None NikonFM2
カメラの機種名でやってみたけど、実践的に考えると特定のsuffixのある文字列を引っ掛けるのに使えそう。もちろん (?=...) には正規表現が使える。
>>> m = re.search(r'Nikon(?=[A-Z0-9]{3})', 'NikonFE2')
>>> m
<_sre.SRE_Match object; span=(0, 5), match='Nikon'>
>>> m.group()
'Nikon'
否定先読みアサーション
(?!...)
...が続かない文字列にマッチする。例えば以下のようになる。
>>> cameras = ['NikonFE', 'NikonFE2', 'NikonFM2']
>>> for camera in cameras:
... m = re.search(r'Nikon(?!FE2)', camera)
... if m is not None:
... print(m.group(0), camera)
... else:
... print('None', camera)
...
Nikon NikonFE
None NikonFE2
Nikon NikonFM2
機種名がFE2でない機種がマッチしている。
特定のsuffixを持たない文字列を引っ掛けるのによさそう。
仕事で<meta property="og:image:type" ~ />ではなく<meta property="og:image" ~ /> を正規表現で引っ掛ける方法を探していて今回のネタに行き着いた、という裏事情がある。
BeautifulSoupを使って属性を条件にしてタグを取得する、というのが絡んでいる処理なのだけど、その振る舞いを勘違いしており先読みアサーション氏には出る幕がなかったというオチがつく。無念。この反省もブログのネタとして活かしたい。
後読み
後読みアサーション
(?<=...)
文字列内の現在位置の前に、現在位置で終わる ... とのマッチがあれば、マッチします。
とある。
(?<=abc)def は abcdef にマッチを見つけます。
>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'
含まれるパターンは、固定長の文字列にのみマッチしなければなりません。
先読みアサーションとは異なって、パターンに正規表現を使うことはできない。
シャーロック・ホームズの相棒だけを探す。
>>> import re
>>> watsons = ['JamesWatson', 'JohnHWatson', 'IBMWatson']
>>> for watson in watsons:
... m = re.search(r'(?<=JohnH)Watson', watson)
... if m is not None:
... print(m.group(0), watson)
... else:
... print('None', watson)
...
None JamesWatson
Watson JohnHWatson
None IBMWatson
同じファミリーネームの人物がなかなか思いつかなくて苦労した。一つだけ人間じゃないのが混ざってるけど気にしない。
否定後読みアサーション
(?<!...)
文字列内の現在位置の前に ... とのマッチがない場合に、マッチします。
とある。また後読みアサーションと同様にパターンに正規表現は使えない。
今度はシャーロック・ホームズの相棒じゃない人たちを探す。
>>> for watson in watsons:
... m = re.search(r'(?<!JohnH)Watson', watson)
... if m is not None:
... print(m.group(0), watson)
... else:
... print('None', watson)
...
Watson JamesWatson
None JohnHWatson
Watson IBMWatson
DNAの二重螺旋構造を見つけてノーベル賞取った人とIBMのすごいやつがマッチした。
後読みアサーションは特定のprefixを持つ(あるいは持たない)文字列をマッチさせる、というような使い方ができそう。