メンチカツには醤油でしょ!!

AWS/Java/Node.js/Spreadsheets/Docker/Jenkins/コミュニティ・勉強会レポを主とした技術系ブログ

java.util.regex.Patternで取ったmatcherのgroupが取得できない

マッチしてるはずなのにIllegalStateException thrown: No match found

たとえば、ある文字列が「画面ID + 定型文字列」であるとして、ここから画面IDを抜き取るという実装を正規表現で行う場合…

jshellで実行してみると

まずは文字列の定義をします。この場合は"Bean"が定型文字列です。

jshell> String actionBeanName = "ScreenIdBean";
actionBeanName ==> "ScreenIdBean"

次に正規表現を定義します。任意の文字列で始まり1文字以上、そして後ろにBeanというパターンです。

jshell> Pattern p = Pattern.compile("^(.+)(Bean)$");
p ==> ^(.+)(Bean)$

matcherメソッドでMatchesオブジェクトを取得しgroup(1)を呼ぶと…なんとエラー!

jshell> p.matcher(actionBeanName).group(1)
|  java.lang.IllegalStateException thrown: No match found
|        at Matcher.group (Matcher.java:645)
|        at Matcher.group (Matcher.java:604)
|        at (#4:1)

確認のためにmatches()を呼ぶもtrue…

jshell> p.matcher(actionBeanName).matches()
$5 ==> true

再度group()を取っても同じくNo match foundエラーです。

一致しているはずなのに取れない理由は…

Matcherのmatches()を呼んだあと、そのオブジェクトでgroupを呼ぶ必要があります。
つまり下記のように、まずmを変数に代入して

jshell> java.util.regex.Matcher m = p.matcher(actionBeanName);
m ==> java.util.regex.Matcher[pattern=^(.+)(Bean)$ region=0,12 lastmatch=]

そのオブジェクトmのmatches()を呼んだ後に

jshell> m.matches()
$7 ==> true

groupを呼ぶと、目的の文字列が抽出できました。

jshell> m.group(1)
$8 ==> "ScreenId"

groupを呼ぶには、一旦matches()を呼ばないといけない

正規表現が必ず一致すると明確な場合、エラーチェックとなるmatches()を省略しがちですが、ここが落とし穴でした。

本来であれば下記のように安全な(?)コーディングを行うので、起こりえないです。

String actionBeanName = "ScreenIdBean";
Matcher m = PATTERN.matcher(actionBeanName);
if (m.matches()) {
String screenId = m.group(1);
// 何かしらの処理.
} else {
// マッチしなかった場合の処理.
}

と、必ずエラーチェックしてたから今まで知らなかったと言い訳しておきます^^;

参考

stackoverflow.com