参考:
https://stackoverflow.com/questions/17971466/java-regex-overlapping-matches
ふつう正規表現のmatchを取るときは、あるマッチで採択された文字列部分は、他のマッチで重複して利用されない。
例えば「 _apple_banana_cherry_
」という文字列から、
/_[^_]+_/
をマッチさせると取れるのは _apple_
と _cherry_
の2つ。
オーバーラップする"_"を利用して _apple_
, _banana_
, _cherry_
の3つを取りたい場合は特殊な指定が必要になる。
pythonの場合
簡単な方法は、正規表現パッケージの regex
を使い、オプション overlapped=True
でマッチさせる。
>>> import regex as re
>>> re.findall("_[^_]+_", "_apple_banana_cherry_")
['_apple_', '_cherry_']
>>> re.findall("_[^_]+_", "_apple_banana_cherry_", overlapped=True)
['_apple_', '_banana_', '_cherry_']
Javaの場合
Javaの場合は標準の正規表現パッケージを利用し、startIndexをずらしていく方法をとるのが簡単。
Matcher m = Pattern.compile("_[^_]+(_)").matcher("_apple_banana_cherry_");
if (m.find()) {
do {
System.out.println(m.group());
} while (m.find(m.start(1)));
}
解説:
/_[^_]+(_)/
で、2番目に出現する _
を ()
で括ってグループ1として取得できるようにしている。
m.start(1)
は、グループ1の先頭インデックスを返す。
m.find(N)
はN番目の文字からマッチを開始するという意味。
なので m.find(m.start(1))
は、m.find()をグループ1の先頭インデックスから開始するという意味になり、
つまり2回目以降のループでは、マッチした文字列の末尾の _
から次のマッチを開始するというループになっている。
note:
どのグループを次回マッチの先頭インデックスとするかの指定は、名前付きグループを使ったほうがわかりやすいかもしれない。
(?<name>PATTERN)
また、より複雑なマッチパターンの場合は非キャプチャグループが必要になるかも。
(?:PATTERN)