#概要
Groovyでは、他のプログラミング言語同様に正規表現を扱うことが出来ます。
また、Javaの正規表現を強力に使いやすい構文に仕上がっています。
Groovyの正規表現には以下の3つの重要なキーワードがあります。
/.../
スラッシュの間に正規表現を格納できます。改行が単純に\n
と言った感じで書けます。
=~
文字列から一致する部分を抜き出します。また()
でグループ化されている部分もちゃんと扱えます。
==~
文字列が指定した正規表現と一致しているかどうかをチェックします。
基本
Closure reg = {String target, String pattern ->
// =~ を実行した結果、java.util.regex.Matcherのインスタンスが返される。
(target =~ pattern).each {
if( it instanceof List) { // つまりグループがある
println "Word: ${it.head()}"
println "Groups:"
it.tail().eachWithIndex {String group ,Integer i ->
println " ${i}: ${group}"
}
} else {
println it
}
}
println "*" * 30
}
// 文字列から、正規表現で該当する箇所を抜き出す方法
reg("groovyisgod", /o(.)(.)/)
reg("oh!grailsisgod", /.rails/)
// 本題とは関係ないけど、同じ文字が並んでいる箇所を抽出するサンプル
// もし同じ文字が3つ並んでいる箇所を抜き出したい場合は/(.)\1\1/となる。
reg("0c04bf9ec1e7abcc6ba8f53354f22dfc19a2bf90", /(.)\1/)
// 文字列が、指定した正規表現と一致するかどうかチェックする方法
// 文字列の存在チェックのために ==~ を使う場合、
// 全体に一致するかどうかが問われるので、最初のパターンはfalseになる。
assert false == ("abc" ==~ /b/)
assert true == ("abc" ==~ /.*b.*/)
上記のコードを実行すると、以下のようになります。
Word: oov
Groups:
0: o
1: v
******************************
grails
******************************
Word: cc
Groups:
0: c
Word: 33
Groups:
0: 3
Word: 22
Groups:
0: 2
******************************
応用
よく使うのにいつも書き方を忘れるので、備忘録としてメモ。
自分がよく使うパターンのテキストファイルを読み込んで、各行の値をゴニョゴニョする、というのを想定しています。
各行に記載されている数字を全て抜き出して、単純に合計するサンプルです。
def texts = [
"This apple is 300 yen",
"This Orange IS 150 yen",
"Is this book 2000 yen? or 1980 yen?",
"I am koji. yen wo tukau yo",
"This apple is 3 euro.",
]
// 数字を含む行だけを抽出する
def withNumbers = texts.findAll { String line ->
(line ==~ /.*[0-9].*/)
}
assert withNumbers.size() == 4
assert withNumbers[0] == 'This apple is 300 yen'
assert withNumbers[1] == 'This Orange IS 150 yen'
assert withNumbers[2] == 'Is this book 2000 yen? or 1980 yen?'
assert withNumbers[3] == 'This apple is 3 euro.'
//抜き出した行の中の文字列から、数字の部分だけを抽出する
assert 4433 == withNumbers.collect{ String line ->
(line =~ /[0-9]+/).collect {String match ->
// 1行に2個以上この正規表現(数字)に該当するものがあれば、その分ここが繰り返される
match
}
// 入れ子のリストになっているのでflattenで普通のリストに変換
}.flatten().collect{
// 計算用にIntegerに変換
it as Integer
}.inject{x,y->
//足し合わせる
x + y
}
// もし、文字列のフォーマットが例えば以下のようにしっかりカンマ区切りで数字が並んていて、3番目の数字だけ抽出してみる。
assert ["30"] == ("10,20,30,40.50" =~ /([0-9]+),([0-9]+),([0-9]+),(.*)/).collect {all, first, second, hit, gomig -> hit}
assert "30" == ("10,20,30,40.50" =~ /([0-9]+),([0-9]+),([0-9]+),(.*)/)[0][3]
//ちなみに、"This"を含む行だけ抽出したい場合、以下のようにすると当然"this"が抽出されない。
assert 3 == texts.findAll{(it ==~ /.*This.*/)}.size()
//埋め込みフラグを使えばOK
assert 4 == texts.findAll{(it ==~/(?i).*This.*/)}.size()