動作確認version: Java11
パターンを含まない部分否定の正規表現
パターンで始まらない文字かチェックする場合
否定先読み
あるパターンを含まない正規表現は、否定先読み?!
を使う。
否定先読みは、パターンの開始位置から前を読んでパターンがマッチしなければtrueになる。
パターンで始まらない正規表現
パターンで始まらないかチェックする場合、否定先読みを使って^(?!パターン)
と書く。
つまり、行頭(=パターンの開始位置の前)の次にパターンがマッチしなければtrueになる。
例えばa-zで始まらない文字をチェックする場合は以下の通り。
jshell> Pattern p = Pattern.compile("^(?![a-z]+)")
p ==> ^(?![a-z]+)
jshell> p.matcher("abc").find()
$2 ==> false
jshell> p.matcher("abc123").find()
$3 ==> false
jshell> p.matcher("123abc").find()
$4 ==> true
find vs matches
Javaのmatches
は全体がマッチしているかどうかをチェックする。^
, $
を書かなくても書いたのと同じ挙動になってしまうため、今回の例示にはわかりづらいので使用しない。
# findだと^を書いていないと正しい結果が得られない
jshell> Pattern.compile("(?![a-z]+)").matcher("abc").find()
$1 ==> true
# matchesだと^を書いていないのに正しく動作する
jshell> "123abc".matches("(?![a-z]+).*")
$2 ==> true
# matchesだと全体がマッチしているかを確認されるので、
# 逆に.*を末尾につけないと空文字にしかマッチしなくなってしまう
jshell> "123abc".matches("(?![a-z]+)")
$3ß ==> false
jshell> "".matches("(?![a-z]+)")
$4 ==> true
パターンを含まない文字かチェックする場合
パターンを含まないかチェックする場合は、パターンの前半に.*
を含めばよく、^(?!.*パターン)
となる。
jshell> Pattern p = Pattern.compile("^(?!.*[a-z]+)")
p ==> ^(?!.*[a-z]+)
jshell> p.matcher("abc").find()
$2 ==> false
jshell> p.matcher("abc123").find()
$3 ==> false
jshell> p.matcher("123abc").find()
$4 ==> false
jshell> p.matcher("123").find()
$5 ==> true
パターンで終わらない文字かチェックする場合
パターンで終わらないかチェックする場合は、否定先読みでもできるが、否定後読み?<!
の方がシンプルになる。
否定後読み
否定先読みとの相違点は、パターンの前を読むか後を読むかの違いで、パターンの開始位置から後を読んでパターンがマッチしなければtrueになる。
jshell> Pattern p = Pattern.compile("(?<![a-z]+)$")
p ==> (?<![a-z]+)$
jshell> p.matcher("abc").find()
$2 ==> false
jshell> p.matcher("abc123").find()
$3 ==> true
jshell> p.matcher("123abc").find()
$4 ==> false
パターンで終わらないかどうかをチェックするにはパターンの開始位置の後に$
がくるかどうかがカギになるため、否定後読みで実装するのがシンプルになる。
否定先読みでの実装
完全否定の正規表現につながるので、あえて否定先読みでも実装してみる。カギになるポイントは同じくパターンと$
が隣り合っているかどうかであるため、パターン自体に$
を含めてしまう。
jshell> Pattern p = Pattern.compile("^(?!.*[a-z]+$)")
p ==> ^(?!.*[a-z]+$)
jshell> p.matcher("abc").find()
$2 ==> false
jshell> p.matcher("abc123").find()
$3 ==> true
jshell> p.matcher("123abc").find()
$4 ==> false
パターン$
を含まない文字がtrueとなる。
パターンと一致しない完全否定の正規表現
完全否定の正規表現にする時も否定先読みを使用する。
パターンで始まらない場合とパターンで終わらない場合を合わせたもの(つまり、パターンで始まらないし終わらない)がパターンと一致しない完全否定になる。文中に含むだけなら問題ない。
jshell> Pattern p = Pattern.compile("^(?![a-z]+$)")
p ==> ^(?![a-z]+$)
jshell> p.matcher("abc").find()
$2 ==> false
jshell> p.matcher("abc123").find()
$3 ==> true
jshell> p.matcher("123abc").find()
$4 ==> true
行頭の次にパターンと行末がくることを否定している。