質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

1回答

4692閲覧

正規表現 挟まれた文字を取得

namnium1125

総合スコア2043

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

2クリップ

投稿2018/01/16 17:57

いつもお世話になっております。m(_ _)m

以下のような文字列があった時、

plain

1aabbabaabb

aに挟まれた一つ以上のbbに挟まれた一つ以上のaを取得し、同時にaならbbならaに変える、というのを正規表現を利用して実現したいです。

つまり上記の場合は

plain

1aaaababbbb

となります。

色々解法はあるかもしれませんが、この問題を考える上で躓いたことがあったのでそのことについて質問させてください。

例えば

plain

1abbaba

に対して、

python

1import re 2 3targets = re.finditer(r"a(b+)a|b(a+)b","abbaba")

としたとき、abbaabaの2つを取れるかなと思ったのですが、実際には最初のabbaしか取得できませんでした。

abbaabaならばちゃんと二つ(abbaaba)取得できるのですが、正規表現でabbabaから、abbaabaを取るにはどうすればいいのでしょうか?

基本的なことですみません…どう検索していいか分からずここで質問させていただきました。

回答よろしくお願いします。m(_ _)m

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

###原因

正規表現による連続サーチでは「直前にマッチした部分は次の検索の対象外になる」という規則があります。なぜかはよく考えてみるとほぼ自明であることに気づきます。例えば

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/16 19:47

編集2018/01/16 23:42
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namnium1125

2018/01/17 00:51

うまく行きました!ありがとうございました。m(_ _)m 後読み、先読みアサーション…やっぱり正規表現って知らないことがたくさん…(^ ^; またいつかわからないことが出てくるかもしれません。その時も差し支えがなければよろしくお願いします。m(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問