No Programming, No Life

プログラミング関連の話題や雑記

Re:改行を含むとGroovyの正規表現がうまく動かない

改行を含むとGroovyの正規表現がうまく動かない - プログラマ的京都生活
によると、複数行になったとたんにGroovyの正規表現がうまく動かなくなる模様。
ということで実際に試してみました。
(動作確認 Groovy Version: 1.5.7 JVM: 1.6.0_10)

実際に試してみた

def text1 = 'a=b'
assert text1 ==~ /a=.*/

def text2 = '''a=b
hello
c=d'''
assert !(text2 ==~ /a=.*/)

うむ、確かにtext2の場合、falseが返ってくるようだ。

Java正規表現

Groovyってことは要するにJavaなので、Java正規表現についてちょっとあたってみた。

定義済みの文字クラス
. 任意の文字 (行末記号とマッチする場合もある)

Pattern (Java Platform SE 6)

ふむふむ、行末記号とは?

行末記号

「行末記号」とは、入力文字シーケンスの行の末尾を指定するときに使用する、1 文字または 2 文字の文字シーケンスです。次の文字が行末記号として認識されます。

* 改行文字 ('\n')
* 直後に改行文字が付いたキャリッジリターン文字 ("\r\n")
* 単独のキャリッジリターン文字 ('\r')
* 次行文字 ('\u0085')
* 行区切り文字 ('\u2028')
* 段落区切り文字 ('\u2029)

UNIX_LINES モードが有効な場合は、改行文字だけが行末記号として認識されます。
正規表現 . は、DOTALL フラグが指定されていない場合、行末記号以外のすべての文字とマッチします。

デフォルトでは、正規表現 ^ および $ は行末記号を無視し、入力シーケンス全体のそれぞれ先頭と末尾だけにマッチします。MULTILINE モードがアクティブになると、^ は入力の先頭、および入力の末尾を除くすべての行末記号の後にマッチします。MULTILINE モードの場合、$ は行末記号の直前、または入力シーケンスの末尾にマッチします。

Pattern (Java Platform SE 6)

これか!つまりJava(Groovy)のデフォルトでは「.」は改行文字にヒットしないよってことですかね。ということは…

改良版

def text3 = '''a=b
hello
c=d'''
assert text3 ==~ /a=(.|\n)*/

予想通りでした。「.」or「\n」にヒットするようにすることでtrueが返却されました。

おまけ

DOTALLフラグとやらを指定すると「.」で行末文字もちゃんと認識してくれるかテストしておこう。
ここを参考にしました。

import java.util.regex.*

def text4 = '''a=b
hello
c=d'''
assert Pattern.compile(/a=.*/, Pattern.DOTALL).matcher(text4).matches()

よかった、ちゃんと動きました。
これ、せっかくGroovyなんで ==~ みたいな演算子で指定できないもんですかねぇ。

追記 (2009-01-18)

Re:Re:改行を含むとGroovyの正規表現がうまく動かない - プログラマ的京都生活
にて私の書いた

これ、せっかくGroovyなんで ==~ みたいな演算子で指定できないもんですかねぇ。

に対して、id:mtoyoshiさんご本人よりさらに返答をいただきました。感謝です。

どうやら、埋め込みフラグなるものがあるらしく、それでシンプルな表現になるらしい。

def text5 = '''a=b
hello
c=d'''
assert text5 ==~ /(?s)a=.*/

うん、確かにここにもそう書いてありますね…。うっかりしてました。
こういう正規表現の方言ってどうにかならないものでしょうかねぇ、といつも思ってしまう。
まぁ、うだうだ言わずに「勉強せぇ!」ってことですかね。