参考:
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)