JavaのMatcher.find()とMatcher.reset()を組み合わせて使う

自作アプリの保守開発をしていた。

TwitMorse ~モールス信号でつぶやこう~

その機能の仕様として、モールス信号化された文字列の中に漢字があるとそれを無視して、モールス信号→カタカナに変換する状態になっていた。
その状態をなんとかしようと先日立ち上がって、アレコレ格闘した。

問題は解決して、無事リリースしたと思いきや・・・

今度は「漢字を含まないモールス信号を復号化できない」というバグが起きた。

そのバグ改修をしていて、JavaのMatcherクラスにある特性があることに気づいたので備忘録と自戒として残すことにした。

JavaのMatcher.find()を呼ぶとマッチ文字列の参照が次へ移ってしまう

そのため、matcher.reset()メソッドを読んであげる必要がある。

以下はそのいい事例を紹介する。

入力文字列

Strign inputText = 正規表現;

Javaプログラム側

失敗した例

   private String fixIncludeKanjiMorseText(String text) {
String inputText = tripMorseTag(text); // " #モールス信号"を一時削除
String regex = "[亜-熙]|[一-龠]"; // 漢字の判定
StringBuffer tunedText = new StringBuffer(inputText);
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(inputText);
if (matcher.find()) {
while (matcher.find()) {
                                Log.d("test", matcher.group()); // デバッグ出力
matcher.appendReplacement(tunedText, matcher.group() + " ");
}
matcher.appendTail(tunedText);
return tunedText.toString().replaceAll(inputText, "");
}
return inputText;
}

このプログラムは、モールス信号と漢字を含む文字列を取得して、漢字があれば後ろに空白を入れるというなんともめんどくさい処理である。
これでLog.dの出力を出すと、

規
表
現

となってしまい、先頭の「正」の字が無視されて処理されなくなっていた。


正しいMatcherの使い方 reset()する

成功したバージョン

    private String fixIncludeKanjiMorseText(String text) {
String inputText = tripMorseTag(text); // " #モールス信号"を一時削除
String regex = "[亜-熙]|[一-龠]";
StringBuffer tunedText = new StringBuffer(inputText);
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(inputText);
if (matcher.find()) { // matcher.findしてしまうと1個参照が進んでしまう
matcher.reset(); // ←ここがポイント!!
while (matcher.find()) {
                                Log.d("test", matcher.group()); // デバッグ出力
matcher.appendReplacement(tunedText, matcher.group() + " ");
}
matcher.appendTail(tunedText);
return tunedText.toString().replaceAll(inputText, "");
}
return inputText;
}

上記が、思い通りに動いたプログラム。

Log.dの出力

正
規
表
現

違いは

matcher.reset();

をしているところだ。

Matcher.find()メソッドの罠として、find()を呼び出すと次のマッチ文字列に参照が移ってしまうという罠があった。

if (matcher.find()){

ここで参照が先に進んでいる。

なので、

「入力文字列中にマッチさせたい文字列があり、その文字列すべてに何らかの処理をする」という場合は

if (matcher.find()){
matcher.reset()

をしてあげる必要がある。

何を言っているか分からないと思いますが、とにかく

JavaのMatcher.find()を呼んだらMatcher.reset()を呼んであげよう

ということが言いたい。

if (matcher.find()){
matcher.reset()

↑を今後忘れないようにして生きたい。

今回のモールス信号+漢字混在の問題はかなり苦労しました・・・。

漢字判定の記事

というわけで今から修正リリースを行います。

ちなみにこんなことができるようになりました

今まではモールス信号テキスト内に漢字が含まれているとその部分だけ無視するようになっていました。
修正する時間とれなくてごめんなさい・・・。


コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください