PatternとMatcherによる正規表現処理

独特なPatternとMatcherの使い方をまとめました。

概要

Java標準ライブラリで正規表現処理を行う場合、「java.util.regex」パッケージのPatternとMatcherがベースになる。ただしユーティリティも用意されているので問題なければその方が楽。こちら

java util regexクラス図

クラス 概要

Pattern

コンパイル済みの正規表現

Matcher

Patternを特定の文字列に適用するときのステートを保持する

正規表現を表すPatternクラスとそれに対応した問い合わせの結果を保持するMatcherクラスを別々にすることで、概念的に分けている。概念的にはスッキリしているが、使いやすいかはどうかは別。

基本手順としては次のようになる。

Pattern pattern = Pattern.compile("a*b");
Matcher matcher = pattern.matcher("aaaaab");
// マッチしているのでtrue
boolean matchResult = matcher.matches();

Patternの生成

次のことをおさえておきたい。

  • Pattern#compile()のファクトリから生成を行う。

  • 正規表現文字列が不正の場合はPatternSyntaxException。

  • 正規表現文字列の他にオプションを指定することができる。
    複数オプションは「|」演算で指定する。

Pattern pattern =
    Pattern.compile("a*b", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

Patternオブジェクトはイミュータブルであるため、マルチスレッドでも使い回し可能。

検索

検索処理は次の3種類のメソッドが用意されている。全てbooleanを返すがパターンの適用範囲がそれぞれ違う。

メソッド 概要

find()

パターンが(どこかの)部分とマッチした場合、true。

lookingAt()

パターンが先頭からマッチした場合、true。
(暗黙的に正規表現が「\A」が先頭に付加されて判定されるようなもの)

matches()

パターンが全体とマッチした場合、true。
(暗黙的に正規表現が「\A…\z」で囲まれて判定されるようなもの)

正規表現の検索処理を想像した場合find()の動きがデフォルトっぽく思えるが、lookingAt()やmatches()も場面によっては有効なので違いを把握して使う。

find()は複数回マッチすることがあるので、MatcherがJDBCのResultSetのように内部的に前回の見つけた位置を保持しているので 2回目のfind()は2回目のマッチ位置を返してくれる。appendReplacement()やappendtail()との併用も可能。

String input = "http://example.com/";
Pattern[] patterns = new Pattern[] { Pattern.compile(".com"), Pattern.compile("http://"), Pattern.compile("http://\\w+.com/") };
for (Pattern pattern : patterns) {
  log.info("input[{}] pattern[{}]", input, pattern);
  log.info("find() result is {}", pattern.matcher(input).find());
  log.info("lookingAt() result is {}", pattern.matcher(input).lookingAt());
  log.info("matches() result is {}\n", pattern.matcher(input).matches());
}

出力

input[http://example.com/] pattern[.com]
find() result is true
lookingAt() result is false
matches() result is false

input[http://example.com/] pattern[http://]
find() result is true
lookingAt() result is true
matches() result is false

input[http://example.com/] pattern[http://\w+.com/]
find() result is true
lookingAt() result is true
matches() result is true

置換

次の2つのメソッドが用意されている。置換された文字列が戻り値。

メソッド 概要

replaceFirst()

最初の部分シーケンスを指定された置換文字列に置き換える。

replaceAll()

指定された置換文字列に置き換える。

String input = "---dog---dog---dog---";
Pattern pattern = Pattern.compile("dog");
String replacement = "cat";

log.info("input[{}]  pattern[{}] replacement[{}]", input, pattern, replacement);
log.info("replaceFirst() replaced [{}]", pattern.matcher(input).replaceFirst(replacement));
log.info("  replaceAll() replaced [{}]", pattern.matcher(input).replaceAll(replacement));

出力

input[---dog---dog---dog---]  pattern[dog] replacement[cat]
replaceFirst() replaced [---cat---dog---dog---]
  replaceAll() replaced [---cat---cat---cat---]

分割

Pattern#split()を使ってString配列に分割できる。

String input = "one-two-three";
Pattern pattern = Pattern.compile("-");
log.info("input[{}]  pattern[{}]", input, pattern);
log.info("split() result is [{}]", Arrays.toString(pattern.split(input)));

出力

input[one-two-three]  pattern[-]
split() result is [[one, two, three]]

ユーティリティメソッドによる正規表現処理

正規表現処理で必ずしもPatternとMatcherを使う必要はない。Stringクラスを中心にの使いやすいユーティリティメソッドが用意されている。ただしPatternの使い回しや細かい処理は当然できないので、そういった場合はPatternとMatcherを使う。

メソッド 対応するユーティリティメソッド

Matcher#matches()

String#matches() or Pattern#matches()

Matcher#replaceAll()

String#replaceAll()

Matcher#replaceFirst()

String#replaceFirst()

Pattern#split()

String#split()

実装としては内部でPatternやMatcherが使われているのがほとんど。