君ならどう書く? s///ge
Perlとかでは、文字列x中の正規表現にマッチする部分を置換するときには
x =~ s/正規表現パターン/置換後の文字列/g;
の様に書きます。Javaでは同様のことを
x = x.replaceAll("正規表現パターン", "置換後の文字列");
と書けます。このとき、置換後の文字列中で、$1, $2といった記法により、パターン中の部分一致した文字列を参照できるので、それなりに強力で便利です。しかし、$1,$2に対してなんらかの加工を行い、その処理結果で置換したい、という場合にはJavaでは相当に面倒になります。たとえばPerlで
x =~ s/正規表現パターン/置換後文字列として任意の式をここに書ける/eg;
の様に簡潔にかけるのに、Javaでは
Pattern p = Pattern.compile("正規表現パターン"); Matcher m = p.matcher(x); StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, 置換後文字列として任意の式をここに書ける) } m.appendTail(sb); x = sb.toString();
のようになります。ちょっとどころではなくうんざりです。上をそのままGroovyで書き下したとしても相当面倒です。
しかし、Groovy JDKで拡張されたString#replaceAll(String, Closure)メソッドを使用すれば以下の様に簡単に書けます。
x = x.replaceAll(/正規表現パターン/) { 置換後文字列として任意の式をここに書ける }
部分マッチを参照するには以下の様にクロージャが引数をとるようにします。
"one cat two cats in the yard".replaceAll(/c(a)t/) { m0, m1 -> "d${m1}ck" }
m1が1番目の部分マッチを参照しており($1相当)、m0はマッチした文字列全体です($0相当)。
x = x.replaceAll(..);
のようにxに改めて置換後の結果を代入しているのは、Stringはイミュータブルだからですね。
便利!
ちなみにRubyだと
x.gsub!(/正規表現パターン/) { | m0 | 置換後文字列として任意の式をここに書ける }
こうかな(試していません)。GroovyのreplaceAllはRubyのString#gsubとほぼ同様ですが、$0以外の部分マッチ文字列である$1,$2をクロージャ引数で取るところが違います。もともとGroovyでは$1,$2のような「暗黙マッチ結果」の参照の方法が無いわけじゃないですけど、長い(Matcher.getLastMatcher().group(n))ですからクロージャ引数を使う上のような方法がすっきりしますね。