要約
- Java1.7以降では
(?<名前>パターン)
と書くことで正規表現のグループに名前をつけることができる。 -
Matcher#group(String name)
でマッチした文字列を取り出せる。
背景
部品に汎用性を持たせるために、正規表現をプロパティファイルなどに外出しにする事があります。
例えば時間 [スレッド名] エラーレベル クラス名 - メッセージ
という形式のログを各要素ごとに抽出したい場合、以下のように書けます
log.pattern=(.+?) \[(.+?)\] (ERROR|INFO|DEBUG) (.*?) - (.*?)
String log = "22:22:50.324 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []";
String patternString = getProperty("log.pattern");
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(log);
if (matcher.matches()) {
String time = matcher.group(1);
String thread = matcher.group(2);
String level = matcher.group(3);
String className = matcher.group(4);
String message = matcher.group(5);
・・・
}
一見汎用的に見えるのですがこの手法には大きな弱点があります。それはグループの順番を変えられないことです。Javaコードでグループの値を取り出すのにグループの順番を指定しているからです。
例えば、以下のようにスレッド名とエラーレベルの順番を入れ替えた途端にコードが破綻します。汎用的にしたつもりだったのに。
22:22:50.324 DEBUG [main] org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []
名前付きの前方参照を行う正規表現グループ
Java1.7から『名前付きの前方参照を行う正規表現グループ』が利用できるようになりました。
(?<名前>パターン)
と書くことで正規表現のグループに名前をつけることができます。
先程の正規表現に適用してみます。
log.pattern=(?<time>.+?) \[(?<thread>.+?)\] (?<level>ERROR|INFO|DEBUG) (?<className>.*?) - (?<message>.*?)
これを取り出すには、Matcher#group(String name)
を使います
String time = matcher.group("time");
String thread = matcher.group("thread");
String level = matcher.group("level");
String className = matcher.group("className");
String message = matcher.group("message");
名前で取得するのでスレッド名とエラーレベルの順番を入れ替えても、正規表現の変更だけで対応できます。
log.pattern=(?<time>.+?) (?<level>ERROR|INFO|DEBUG) \[(?<thread>.+?)\] (?<className>.*?) - (?<message>.*?)
地味ですが便利な機能ですね。