トリックオアトリート!!
お菓子あげないからいたずらしてください、あらっきぃです。
10月31日に記事を上げるはずがいつの間にか二日も経っていました。何てこった。
ノリが季節外れな感じがしますが気にしないでください。
JavaScriptのString.prototype.replace
JavaとJavaScriptは違います。が、ときとしてJavaでJavaScriptのようなことをしたくなりたくなることがあります。
……というか、JavaScriptのString.prototype.replace
メソッド便利すぎです。for
やwhile
を用いずに全てのマッチに対して処理できるわけですから。こんな風に、
var
str = 'Trick or Treat',
rep = str.replace(/[a-zA-Z]/g, function (c, i, str) {
console.log("%s:%d:%s", c, i, str);
return 'a' <= c && c <= 'z' && c.toUpperCase() ||
'A' <= c && c <= 'Z' && c.toLowerCase();
});
console.log(rep);
実行するとこんな結果になります。
T:0:Trick or Treat
r:1:Trick or Treat
i:2:Trick or Treat
c:3:Trick or Treat
k:4:Trick or Treat
o:6:Trick or Treat
r:7:Trick or Treat
T:9:Trick or Treat
r:10:Trick or Treat
e:11:Trick or Treat
a:12:Trick or Treat
t:13:Trick or Treat
tRICK OR tREAT
めったに使いませんがコールバックの引数として、マッチした文字列
とマッチしたグループ
のあとにマッチした位置
ともとの文字列
が入ってきます。
便利ですよね~。
Javaって…
これと同じことをJavaでやるとですね…
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexSample {
static public void main(String... args) {
String str = "Trick or Treat";
Pattern pat = Pattern.compile("[a-zA-Z]");
Matcher m = pat.matcher(str);
StringBuilder sb = new StringBuilder();
int start = 0;
while(m.find()) {
System.out.printf("%s:%d:%s\n", m.group(), m.start(), str);
sb.append(str.substring(start, m.start()));
start = m.end();
char c = m.group().charAt(0);
if ('a' <= c && c <= 'z') sb.append((char)(c - 0x20));
else if ('A' <= c && c <= 'Z') sb.append((char)(c + 0x20));
}
sb.append(str.substring(start));
System.out.println(sb.toString());
}
}
こんな感じでしょうか? そもそも正規表現を使うのがナンセンスな気がします。
ちょっとwrapしてみる
なのでこんなクラスを用意してみました。
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Replacer {
static String replace(String str, Pattern pat, ReplaceCallback callback) {
StringBuilder sb = new StringBuilder();
Matcher m = pat.matcher(str);
int start = 0;
while (m.find()) {
sb.append(str.substring(start, m.start()));
String matched = str.substring(m.start(), m.end());
start = m.end();
ArrayList<String> groups = new ArrayList<>();
for (int j = 0; j <= m.groupCount(); j++) {
groups.add(m.group(j));
}
sb.append(callback.replaceString(matched, groups, str, pat, m.toMatchResult()));
}
sb.append(str.substring(start));
return sb.toString();
}
}
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
public interface ReplaceCallback {
String replaceString(String matched, List<String> groups, String str, Pattern pat, MatchResult matchResult);
}
これを使うと、さっきのコードをこんな風に書きなおすことができます。
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
public class RegexSample {
static public void main(String... args) {
String str = "Trick or Treat";
Pattern pat = Pattern.compile("[a-zA-Z]");
String rep = Replacer.replace(str, pat, new ReplaceCallback() {
@Override
public String replaceString(String matched, List<String> groups,
String str, Pattern pat, MatchResult matchResult) {
System.out.printf("%s:%d:%s\n", matched, matchResult.start(), str);
char c = matched.charAt(0);
c = (char)('a' <= c && c <= 'z' ? c - 0x20 :
'A' <= c && c <= 'Z' ? c + 0x20 : c);
return Character.toString(c);
}
});
System.out.println(rep);
}
}
どうでしょうか? さっきよりもJavaScriptのコードに近づいて、読みやすくなった気がします。
今回載せたコードは全てパブリックドメインとして煮るなり焼くなり好きにしてやってください。
TODO
JavaDocを書く。