今回は正規表現について。 『Groovyイン・アクション』Chapter3 を参考にしています(一覧)。

- 作者: Dierk Konig,Andrew Glover,Paul King,Guillaume Laforge,Jon Skeet,杉浦孝,櫻井正樹,須江信洋,関谷和愛,佐野徹郎,寺沢尚史
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2008/09/27
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 146回
- この商品を含むブログ (121件) を見る
パターンの定義
まずはパターンの定義方法を。 パターンは単なる文字列なので、前回見た文字列の5つの定義方法のいずれかで、普通に文字列を定義すればいいだけです。 ただし、「スラッシュ構文」を用いて定義するのが通例のようです(というか、スラッシュ構文は正規表現のパターンを定義するためにある)。
スラッシュ構文はほとんどエスケープがいらないので、一度慣れると Java には戻れなくなる恐れがあります:~)
assert "abc" == /abc/; assert "\\d" == /\d/;
また、スラッシュ構文は GString 対応なので、「ドル構文」も使えます:
def reference = "hello"; assert reference == /$reference/;
「パターンの終わりを意味しないドル記号」はエスケープの必要なし。
assert "\$" == /$/;
パターン・マッチ
「パターン・マッチ」とは、ある文字列が指定されたパターンに(全体として)一致するかどうかを評価する操作。
パターン・マッチ演算子 : ==~
パターン・マッチ演算子 (==~, !=~) は、(非)等値演算子 ( ==, != ) と同じように、評価結果は Boolean 値。
twister = 'she sells sea shells at the sea shore of seychelles'; value = (twister ==~ /\w+( \w+)*/); assert value instanceof java.lang.Boolean; // 「パターン・マッチ」の評価結果は Boolean assert twister ==~ /\w+( \w+)*/; // twister がパターンに一致するかどうか assert twister !=~ /s.e/; // twister がパターンに一致しないかどうか
switch-case, grep()
「パターン・マッチ」は、上記の他に
- switch-case 文
- Collection に対する grep()
でも使われます。 注意が必要なのは、これらに使用されるのは(パターンを表す)文字列ではなく、java.util.regex.Pattern オブジェクトだということです。 後で少し詳しくやりますが、文字列を Pattern オブジェクトに変換するには、単にその文字列の前にパターン演算子 (~)*1 を付けるだけです。
assert (~/..../ ).isCase('bear'); // 通常、こういう使い方はしない //***** switch-case ***** switch('bear'){ case '....' : // ここに一致するのは switch('....') のとき assert false; break; case ~/..../ : // 4文字の文字列に一致する assert true; break; default: assert false; } //***** grep() ***** beasts = ['bear', 'wolf', 'tiger', 'regex']; assert beasts.grep(~/..../) == ['bear', 'wolf'];
パターン検索
「パターン検索」とは、ある文字列に対して指定されたパターンに一致する部分を検索する操作。 検索結果の文字列に対して置換などの操作も行うことが可能。
パターン検索 : =~
パターン検索を行うための「パターン検索演算子 (=~)」の基本的な使い方。 Matcher オブジェクトが生成されているのですが、そんなことより、文の形を覚えてしまった方が良いかと。 Matcher クラスの使い方については後述。
myFairStringy = 'The rain in Spain stays mainly in the plain!'; rhyme = /\b\w*ain\b/; //***** パターン検索 (1) ***** found = ''; (myFairStringy =~ rhyme).each{ match -> found += match + ' '; } assert found == 'rain Spain plain '; //***** パターン検索 (2) : グループを使う ***** ('xy' =~ /(.)(.)/).each{ all, x, y -> assert all == 'xy'; assert x == 'x'; assert y == 'y'; }
String × パターン検索
Groovy では、正規表現関連のメソッドがいくつか String クラスに定義されてます(Groovy JDK 参照)。 ここでは、その中から Pattern クラスに関係しないメソッドを見ていきます(Pattern クラス関連のメソッドは後述):
メソッド | 戻り値 |
---|---|
eachMatch(String, Closure) | String |
splitEachLine(String, Closure) | Object |
find(String) find(String, Closure) |
String |
findAll(String) findAll(String, Closure) |
List |
replaceAll(String, Closure) | String |
サンプル・コードをいくらか:
myFairStringy = 'The rain in Spain stays mainly in the plain!'; rhyme = /\b\w*ain\b/; //***** eachMatch(String, Closure) ***** found = ''; myFairStringy.eachMatch(rhyme){ match -> found += match + ' '; } assert found == 'rain Spain plain '; //***** split(String) ***** words = myFairStringy.split(rhyme); assert words == ['The ', ' in ', ' stays mainly in the ', '!']; // 返り値は String[] //***** find(String), find(String, Closure) ***** xain = myFairStringy.find(rhyme); assert xain == 'rain'; xain = myFairStringy.find(rhyme){ it - 'ain' + '___'; } assert xain == 'r___'; //***** findAll(String), findAll(String, Closure) ***** xains = myFairStringy.findAll(rhyme); assert xains == ['rain', 'Spain', 'plain']; xains = myFairStringy.findAll(rhyme){ it - 'ain' + '___'; } assert xains == ['r___', 'Sp___', 'pl___']; //***** replaceAll(String), replaceAll(String, Closure) ***** result = myFairStringy.replaceAll(rhyme, '???'); assert result == 'The ??? in ??? stays mainly in the ???!'; result = myFairStringy.replaceAll(rhyme){ it - 'ain' + '___'; } assert result == 'The r___ in Sp___ stays mainly in the pl___!';
Pattern クラスと Matcher クラス
ここでは、Pattern クラスと Matcher クラスの Groovy 拡張を見ていきます。
Pattern クラス
String オブジェクトに対してパターン演算子 (~) を適用すると、Pattern オブジェクトを取得することができます。 Java と同様に、パターンを使い回すときはこの Pattern オブジェクトを保持して使用すると効率的です。
twister = 'she sells sea shells at the sea shore of seychelles'; //***** パターン・コンパイル : ~ / bitwiseNegate() ***** pattern = ~/\w+( \w+)*/; assert pattern instanceof java.util.regex.Pattern; //***** パターン・マッチ : String#matches(Pattern) ***** assert twister.matches(pattern); //***** パターン検索 : String#replaceAll(Pattern, String) ***** pattern = ~/\w+/; wordsByX = twister.replaceAll(pattern, 'x'); assert wordsByX == 'x x x x x x x x x x';
String に定義されているメソッドで、Pattern オブジェクトを使用するものを列挙しておきます(Groovy JDK より):
メソッド | 戻り値 |
---|---|
matches(Pattern) | boolean |
eachMatch(Pattern, Closure) | String |
splitEachLine(Pattern, Closure) | Object |
find(Pattern) find(Pattern, Closure) |
String |
findAll(Pattern) findAll(Pattern, Closure) |
List |
replaceAll(Pattern, String) replaceAll(Pattern, Closure) |
String |
replaceFirst(Pattern, String) | String |
Matcher クラス
Java では、Matcher クラスはかなり使いにくかったイメージがありますが(個人の感想です)、Groovy では getAt() メソッドによって配列っぽくアクセスできるので、だいぶ使いやすくなってる感が。
matcher = ('a b c' =~ /\S/); // 括弧なくても OK assert matcher instanceof java.util.regex.Matcher; //***** asBoolean() ***** assert matcher; //***** getAt(), getCount() ***** assert matcher[0] instanceof String // グループがないときは String assert matcher[1] == 'b'; assert matcher[0] == 'a'; // アクセスの順番は任意 assert matcher[2] == 'c'; assert matcher[1..2] == ['b' , 'c']; assert matcher.count == 3; //***** グループの使用 ***** matcher = 'a:1 b:2 c:3' =~ /(\S+):(\S+)/; // () を使ってグループを2つ定義 assert matcher.hasGroup(); assert matcher[0] instanceof List; // グループがあるときは List<String> assert matcher[0] == ['a:1', 'a', '1']; assert matcher[1] == ['b:2', 'b', '2']; assert matcher[2] == ['c:3', 'c', '3'];