###原因
正規表現による連続サーチでは「直前にマッチした部分は次の検索の対象外になる」という規則があります。なぜかはよく考えてみるとほぼ自明であることに気づきます。例えば
re.finditer(r"a", "a")
を考えてみると、最初にマッチした位置span=(0, 1)
が次回の検索で含められてしまうと、いつまでたっても同じ位置がマッチし続けてしまいます。これでは意味がないですよね・・・
###対処
aで挟まれたbを探す際に両端のaをマッチ対象に含めないように配慮する必要があります。そういうことをするには先読み、後読みパターンを使うのが便利です。先読み、後読みパターンは言語、ツールなどによってサポートされていたりされていなかったりする割合後発の機能のようですが、
https://docs.python.jp/3/library/re.html
を見るとPythonのreモジュールではちゃんとサポートされていました。
具体的にはこのように使います。
r"(?<=a)(b+)(?=a)"
Pythonのリファレンスでは(?<=a)
は後読みアサーション、(?=a)
は先読みアサーションと呼ばれています。本件での大雑把な意味は
(?<=a)
=> 「後続するパターンの直前のパターンを指定するが、そのパターン自体はマッチ対象に含まない」
(?=a)
=> 「直前のパターンの直後のパターンを指定するが、そのパターン自体はマッチ対象に含まない」
といった感じです。この表現(仕様の捉え方)はかなり大雑把です。使いこなすにはより正確な意味を把握しておいた方がよいと思います。後読みアサーションは「既にマッチ済みであってもその部分文字列も含めて前に戻って後端部分がマッチしているかをチェックしてくれ、かつ後読みアサーションにマッチした部分はマッチ結果に含めない」先読みアサーションは「後続する文字列がマッチするかをチェックするがマッチしても検索中の文字列からマッチ済みとして消費されない」というような捉え方もできると思います。
結局、次のようにできます。
Python
1import re
2
3for m in re.finditer(r"(?<=a)(b+)(?=a)|(?<=b)(a+)(?=b)", "abbaba"):
4 print(m)
5
6==>
7<_sre.SRE_Match object; span=(1, 3), match='bb'>
8<_sre.SRE_Match object; span=(3, 4), match='a'>
9<_sre.SRE_Match object; span=(4, 5), match='b'>
10
余談:
先読みは「消費されない」と言いましたが、じゃぁ(?=a)
だけのパターンだと無限にマッチし続けるんでしょうか?ちょっと実験してみました。
python
1>>> it = re.finditer(r"(?=a)", "a")
2>>> it.__next__()
3<_sre.SRE_Match object; span=(0, 0), match=''>
4>>> it.__next__()
5Traceback (most recent call last):
6 File "<pyshell#20>", line 1, in <module>
7 it.__next__()
8StopIteration
実際やってみるとちゃんと停止します。マッチした文字列範囲がspan=(0, 0)
となってはいますが同じ場所から検索するのでは意味がないということでちゃんと次の位置から探そうとしてくれるのですね。「ライブラリーの動作というのは合理的にうまいことできているもんだなぁ」と感心します・・・
ただ、そういう意味では「原因」のところに書いた「直前にマッチした部分は次の検索の対象外になる」という説明は不正確であると思います。「分かり易さのための大雑把な説明」という程度に捉えていただければと思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/17 00:51