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つ作って結合であまり良くないかも。