Javaでの正規表現で特定の文字列を含まないマッチのさせ方。
正規表現で1文字単位での特定の文字を含まないマッチのさせ方は知っていたけど
特定の文字「列」を含まないマッチのさせ方は知らなかったのでメモ。
否定的先読みの機能を使ってマッチさせることによって実現可能になる。
※先読みが使えない場合のやり方もあるけどそれはまだ調べていない。
実際には
// 否定的先読み Pattern p = Pattern.compile("hoge(?!pattern)");
こんな感じで書いてやってhoge(pattern以外の文字列)という形でマッチさせるという感じ。
実際にこれを何に使ったかというと、solrのクエリを解析するにあたって
field:((?!field)*)
という感じで取りだすことに使った。
一番最初は
(field:(.*))
でインジャネ?って思ったんだけど
これだといとも簡単に最長マッチしちゃって
field:xxx AND field:yyy AND field:zzz&...というクエリがあった場合
最後まで取ってきてしまう。文字列全体でマッチしてしまう。
そこで最短マッチなんじゃね?って思って安直に
(field:(.*?))
こうしてみた
こうするとちゃんとフィールドの出現回数分(この例だと3回)マッチするんだけど
肝心のフィールド指定の文字列が空文字になってしまう。
(0回以上)だからダメなのかとおもい+にしてみると最初の一文字しか取ってこない。
そこで、「後ろに続くであろう文字列」を指定して最長マッチを仕掛けてみると
(field:(.*))([+ ]AND|[+ ]OR|[+ ]NOT|[&)}])
「field:xxx AND field:yyy AND field:zzz&」
あれ?全部マッチしちゃったぞ?
じゃあ*を最短マッチにすればどうかな?
(field:(.*?))([+ ]AND|[+ ]OR|[+ ]NOT|[&)}])
「field:xxx」おー、上手く取れた。これでOKかな?と思っていたが
クエリにはまださまざまなパターンがあった。
スペースや+で終了しているもの
field:xxx AND field:yyy AND field:zzz &
ANDやORを()でくくっているもの
(field:xxx AND field:yyy) AND field:zzz &
複数キーワードがANDやORでつながれていないもの
field:xxx yyy AND field:zzz&
field:xxx field:yyy field:zzz&
これらすべてをキチンとマッチさせる正規表現を考えていくと
(field:)(((?!field|[ +]AND|[ +]NOT|[ +]OR)[^)&}])*)([)&+ }]|[ +]AND|[ +]OR|[ +]NOT)
こうなった。ここまで到達するのにスゲー時間がかかった。
正規表現ムジー(゚ω゚ )
これをやったことで気付かされたのが、
「特定のパターンを取りだすためには開始と終了のパターンを指定する必要がある」
ということ。上の例だと「field:」が開始のパターンで、ANDやらORやらを指定している部分が終了のパターン。
指定しなくてもできるのかもしれないけど少なくとも俺にはやり方がわからなかった。
確かにいままでsedとかでいろいろ取ってくる場合は全部指定していた。
指定しなくても上手く取ってくるとかできないのかなぁ。