Java8 では、正規表現で分割 split したストリームは取得できるけど、
java.util.regex.Pattern の splitAsStream(CharSequence input) ⇒ Stream<String>
マッチした文字列のストリームは標準では提供されていない。
自然に Matcher の find() 実行ループをメソッドにすれば、
public static void results(String regex, String input, Consumer<String> c){
Matcher m = Pattern.compile(regex).matcher(input);
while(m.find()){
c.accept(m.group());
}
}
Consumer<String> にマッチした文字列を順番に処理するラムダは書ける。
Stream 生成するには、↑をリストに一旦格納するよりも、、
Java8 では、java.util.regex.Matcher から、spliterator を生成するなどの方法ぐらいしか
良い方法はない。
public static Stream<String> matchToStream(Pattern pattern, CharSequence target){
Matcher m = pattern.matcher(target);
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>(){
@Override
public boolean hasNext(){
return m.find();
}
@Override
public String next(){
return m.group();
}
}, Spliterator.ORDERED), false);
}
Java9 以上なら、java.util.regex.Matcher に、
String replaceAll(Function<MatchResult, String> replacer) や、
Stream<MatchResult>を取得する results()
があって、
Function で置換する為のストリームがあって便利だが、
MatchResult で、start() end() group() は参照できても
何番目のマッチなのか?までは、AtomicInteger など外にカウンタを置かないと
認識できない。
Java8 でもStream<MatchResult>を取得するものを
yipuran-core の中に用意はした。。。
org.yipuran.regex.MatcherStream#findMatches(Pattern pattern, CharSequence input)
けど、何番目のマッチなのか?までは、相変わらずこのままでは取得できない。
Stream<MatchResult>を取得するだけなのだから当然である。
Stream<MatchResult>から collect(Collector.toList()) でリストにすれば順番が確定はするが、
その後で、再び Stream の処理をするのはナンセンスである。
BiConsumer でカウンタ付きの Consumer
public void resultMatch(String regex, String target, BiConsumer<MatchResult, Integer> b){
AtomicInteger i = new AtomicInteger(0);
Pattern.compile(regex).matcher(target).results().forEach(m->b.accept(m, i.getAndIncrement()));
}
なんか、メソッドを作るほどの事でもない。。。
でも、何番目にマッチするかを認識しながら、置換を行う場合、、、
public static String replace(String regex, String string, BiFunction<String, Integer, String> f){
Matcher m = Pattern.compile(regex).matcher(string);
AtomicInteger i = new AtomicInteger(0);
AtomicInteger x = new AtomicInteger(0);
return StreamSupport.stream( Spliterators.spliteratorUnknownSize(new Iterator<String>(){
@Override
public boolean hasNext(){
return m.find();
}
@Override
public String next(){
return string.substring(i.getAndSet(m.end()), m.start()) + f.apply(m.group(), x.getAndIncrement());
}
}, Spliterator.ORDERED), false)
.collect(Collectors.joining()) + string.substring(i.get());
}
あるいは、yipuran-core の MatcherStream.findMatches を使用して、、
public static String replace(String regex, String string, BiFunction<MatchResult, Integer, String> f) {
AtomicInteger i = new AtomicInteger(0);
AtomicInteger x = new AtomicInteger(0);
return Stream.concat( MatcherStream.findMatches(Pattern.compile(regex), string)
.collect(()->new ArrayList<String>(), (r, t)->{
r.add( string.substring(i.getAndSet(t.end()), t.start()) + f.apply(t, x.getAndIncrement()) );
},(r, u)->{}).stream(), Stream.of(string.substring(i.get())))
.collect(Collectors.joining());
}
これだと、Stream.concat で Stream を2つ作って結合であまり良くないかも。