EeBlog(テクニカルブログ)

第4回 括弧で囲まれた範囲を判断する(正規表現)

皆さんは以下の処理を書けますか?

文字列「この{ABC}とあの{DEF}は同じ原料です。」が在ります。
この文字列の{}で囲まれた部分を「肉と野菜」で置換してください。
実行結果は「この肉と野菜とあの肉と野菜は同じ原料です。」 となります。

先ず、{}で囲まれた範囲を判断する方法が解らなければなりません。
次に「置き換える」という処理も解らなければいけません。

後者は単純な文字列操作ですが問題は 前者、「{}で囲まれた範囲を判断する」ことでしょう。

文字列の前から1文字ずつ診ていき、 「{」が出てきたらフラグを立て次の「}」が出てきた地点の位置までを 置き換える。
……..といった方法もありますが、かなり力技でしょう。
そこで、勘のよい人なら「正規表現」を思い付くのではないでしょうか?

単純に「{」から「}」までで中間は任意の文字列ということであれば、
「\{.*\}」となります。
「{」と「}」は正規表現の特殊文字ですから「\」を付けてエスケープが必要です。
「.」は任意の文字「*」は前の文字の0回以上の繰り返しですから 「.*」で任意の文字列となります。

以下のようなコードを書いてみます。

public class PatternReplaceTest {

     public static void main(String[] args) {

         String s = "この{ABC}とあの{DEF}は同じ原料です。";
         s = s.replaceAll("\\{.*\\}", "肉と野菜");
         System.out.println(s);

     }

}

結果を確認してみると…….。

この肉と野菜は同じ原料です。

となってしまいます。
もちろんNGです。

これは、最初の「{」と最後の「}」を対としてしまったからです。
そこで正規表現の「最長一致数量子」を利用します。
( 「java.util.regex.Pattern」クラス のAPIを参照 )
最大で幾つ目の一致で「一致した」とみなすかを指定します。
最初の1つ目で1つの一致とみなしたい場合「?」を追加して
「\{.*?\}」といった正規表現を使います。

public class PatternReplaceTest {

     public static void main(String[] args) {

         String s = "この{ABC}とあの{DEF}は同じ原料です。";
         s = s.replaceAll("\\{.*?\\}", "肉と野菜");
         System.out.println(s);

     }

}

これで、肉と野菜が同じ原料なんて事はなくなりました…。