導入
Javaで文字列の検索や抽出を行う際、皆さんはどのように実装していますか?単純な置換であればStringクラスのメソッドで十分ですが、複雑なパターンマッチングやグループ抽出を行うには、java.util.regexパッケージのPatternクラスとMatcherクラスが不可欠です。特にMatcherクラスは、単なる判定機ではなく「マッチングエンジンの状態」を保持するオブジェクトです。これを理解することで、大量のログ解析や複雑なテキスト処理を効率的かつ安全に行えるようになります。
基礎知識
正規表現を扱う際、Javaでは「Pattern」と「Matcher」という2つのクラスを使います。
Patternは、正規表現をコンパイルした後の「不変(Immutable)な設計図」です。一方、Matcherは、その設計図を元に特定の入力文字列に対してマッチングを行う「実行エンジン」です。
重要なのは、Matcherは内部に「どこまで検索したか」という状態(カーソル位置)を保持している点です。これにより、findメソッドを繰り返すことで、文字列内を次々とスキャンすることが可能になります。また、Java 7から導入された「名前付きグループ」機能を使うと、インデックス番号ではなく名前で抽出結果にアクセスでき、コードの可読性と保守性が大幅に向上します。
実装/解決策
Matcherを使いこなすための手順は以下の通りです。
1. Pattern.compile(regex) でパターンを生成する。
2. pattern.matcher(input) でMatcherインスタンスを生成する。
3. find() メソッドでマッチ箇所を順次検索する。
4. group(name) または group(index) で抽出した文字列を取得する。
ここで注意すべきは、find()メソッドは呼び出すたびに状態(カーソル)が更新されるという点です。ループ処理の中でこのメソッドを使うことで、文字列内の全一致箇所を効率よく処理できます。
サンプルプログラム
以下のコードは、名前付きグループを使用して、日付形式の文字列から「年・月・日」を抽出する例です。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexExample {
public static void main(String[] args) {
// 名前付きグループ (?
String regex = “(?
String input = “本日の日付は2023-10-27です。次回は2023-11-01を予定しています。”;
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
// find()でマッチ箇所を走査(状態が保持されるためループ可能)
while (matcher.find()) {
// 名前を指定してグループを取得
String year = matcher.group(“year”);
String month = matcher.group(“month”);
String day = matcher.group(“day”);
System.out.println(“抽出結果: ” + year + “年” + month + “月” + day + “日”);
}
}
}
応用・注意点
現場でよくある失敗は、Matcherのインスタンスをスレッド間で共有することです。前述の通り、Matcherは内部状態(カーソル位置)を保持しているため、マルチスレッド環境で一つのMatcherを使い回すと予期せぬ動作を引き起こします。Matcherは必ずメソッド内のローカル変数として生成するようにしてください。
また、大量の文字列をループ内で処理する場合は、Patternオブジェクトをstatic finalフィールドとして定義し、再利用(コンパイルコストの抑制)を心がけましょう。正規表現の複雑さによってはパフォーマンスに直結しますので、必要以上に複雑なネストは避け、名前付きグループを活用して保守性の高いコードを書くことが、シニアエンジニアとしての腕の見せ所です。

コメント