Tutorial - Regular Expression

StringRegExp()の謎を解き明かすちょっとしたガイド。

StringRegExp( "test", "pattern" [, flag ] )

"test" = 検索対象の文字列。
"pattern" = 特定の文字キーからなる文字列で関数に正確にあなたが検索したいものを知らせます。もしも、かつ、ただし...はあり得ません。一致するか、それともしないかです。
flag[オプション] = "pattern"が見つかったことのみを通知するのか、最初の一致ヵ所を返すのか、"test"文字列内の全ての一致ヵ所返すのかを関数に設定します。

基本

すでに判っているかもしれませんが、StringRegExp()(以下SRE)の呼び出しで難しい部分は "pattern "文字列だけです。関数に文字を使って検索文字列を教えることがパターンであると考えるのが一番いいでしょう。ある文字を見つける方法は色々あります。文字列 "test "を検索したいとしましょう。とても単純です。SREに最初に文字列から "t "を検索しろと命令します。もし見つかったら検索文字列が存在すると仮定して、不一致と判定されるまでパターンの残りが使用されます。つまりもし次の文字が "e "であればまだ一致しているということです。その次の文字が "x "だったとしましょう。SREはただちに一致していないことに気が付きます。検索する文字列の3文字目は "s "だからです。
 

例 1

MsgBox(0, "SRE Example 1 Result", StringRegExp("text", 'test'))

この例ではメッセージボックスには"0"が表示されます。これはテスト文字列"text"の中にパターン"test"はみつからなかったということを表しています。ちょっと単純すぎますが、なぜみつからなかったはもうわかりますね。

次のパターン指定方法はセット("[ ... ]")の使用です。セットは論理関数ORと同じと考えていいでしょう。先ほどの例で使ってみましょう。文字列"test"、文字列"text"を検索したいとします。SREになったつもりでパターン検索を始めてみましょう。最初の検索文字は"t"、次は"e"で検索文字は両方の検索文字列で同じです。次に"s"もしくは"x"を検索したいので"[sx]"のセットを使用します。これは"s"または"x"を表します。そして最後の文字は再び"t"です。

例 2

MsgBox(0, "SRE Example 2 Result", StringRegExp("text", 'te[sx]t'))
MsgBox(0, "SRE Example 2 Result", StringRegExp("test", 'te[sx]t'))

これらは両方とも結果"1"を表示します。パターンが"test"と"text"の両方と一致するからです。

同じように各文字が何回登場するかを"{登場回数}"または"{最小, 最大}"で範囲を指定できます。下に示すのは冗長ですが実例を示しています。

例 3

MsgBox(0, "SRE Example 3 Result", StringRegExp("text", 't{1}e{1}[sx]{1}t{1}'))
MsgBox(0, "SRE Example 3 Result", StringRegExp("aaaabbbbcccc", 'b{4}'))



ちょっとした応用

あなたは今、"単なるStringInStr()関数の進化版じゃないのか?"と考えているかもしれません。"flag"の値を0にして使用するとほとんどの場合でそれは正しいです。しかしSREはあなたが考えているよりももっとパワフルです。SREを使えば使うほどより少ない情報で文字列を探せることに気づくでしょう。またパターンで指定する各文字についてより少なく指定できることもわかります。例としてゲームチャットログの"Gnarly Monster hits you for 18 damage."という文字列を考えて見ましょう。ここからGnarly Monsterがあなたにいくつのダメージを与えたかを知りたいとします。"18"を探しているわけではないのでStringInStr()は使えません。探しているのは"????"で、?には任意の数字がはいります。

ここに私がどのようにパターンを組み立てたかを示します。まず検索文字列についてわかっていることと、わかっていないことを整理しておきましょう。
1) 常に数字のみからなるということ
2) 2文字になることがあるということ
2a) ゲームプレイの経験からモンスターによる最大ダメージは999であるということ
2b) モンスターによる最小ダメージは0であるということ
3) 1文字から3文字の間であるということ
4) 検索対象文字列に他に数字が含まれていないということ

この時点でフラグ値"1"とグループ指定文字"()"を使用することにしました。フラグ値"1"はパターン検索させるだけでなく、見つけた文字"グループ"を各要素に格納した配列を返すようにSREに指定することを意味します。すこし話がそれますがこの例を見てください。

例 4

$asResult = StringRegExp("This is a test example", '(test)', 1)
If @error == 0 Then
    MsgBox(0, "SRE Example 4 Result", $asResult[0])
EndIf
$asResult = StringRegExp("This is a test example", '(te)(st)', 1)
If @error == 0 Then
    MsgBox(0, "SRE Example 4 Result", $asResult[0] & "," & $asResult[1])
EndIf

 

まずパターンは検索対象文字列の一部に一致します。するとSREは全てのグループ("()")を"キャプチャ"するように指定されているので、それらを返し値用配列に保存していきます。例4の2番目のコードのように複数のグループをキャプチャすることも可能です。

Gnarly Monsterの話に戻りましょう。どのようにテキストを"キャプチャ"するのかはわかりました。パターンの作成を始めましょう。探しているのは数字だということがわかっています。"任意の数字と一致"を指定する方法には"[:digit:]"、"[0-9]"、"\d"の3つがあります。最初のものがおそらく1番理解しやすいでしょう。文字の種類指定のために使用できるクラス(digit、alnum、spaceなど。全クラスのリストはヘルプファイルを参照)があり、その1つがdigitです。"[0-9]"は数字の0から9までの全ての数字を範囲指定しているだけです。"\d"は前の2つと同じ意味を持つ特殊文字です。この3つに違いは無く、SREではそれぞれに通常少なくとも2つのパターン構成方法があります。

検索したいのは数字であることがわかっています。まず左カッコ"("を書きます。次に1文字から3文字であり、全て数字であることがわかっているのでパターンは"([0-9]{1,3}"のようになります。最後にグループの終わりを表すために右カッコで閉じて"([0-9]{1,3})"となります。やってみましょう。

例 5

$asResult = StringRegExp("Gnarly Monster hits you for 18 damage.", _
                               '([0-9]{1,3})', 1)
If @error == 0 Then
    MsgBox(0, "SRE Example 5 Result", $asResult[0])
EndIf

 

実行するとメッセージボックスに正しく"18"と表示されます。

次に非キャプチャグループについて説明します。"("の代わりに"(?:"を使用することで非キャプチャグループを定義できます。ログが"You deflect 36 of Gnarly Monster's 279 damage."の場合を考えましょう。これに対して例5のSREを実行すると、"279"の代わりに"36"が返ってきます。ここでやりたいことは2つの数字の間の違いを定義することです。私が思いついたのは2つめの数字は常に後ろにスペースと"damage"という単語がつくということです。したがって先ほどのパターンを"([0-9]{1,3} damage)"と修正します。ただしスクリプトで検索したいのはダメージの値だけで数字の後に付属する" damage"は必要ありません。このような場合に非キャプチャグループを使用します。

例 6

$asResult = StringRegExp("You deflect 36 of Gnarly Monster's 279 damage.", '([0-9]{1,3})(?: damage)', 1)
If @error == 0 Then
    MsgBox(0, "SRE Example 6 Result", $asResult[0])
EndIf

 

すこし長くなりますが正規表現がどのように動作するのか、SREがどのように"考える"のかについての見取り図を描いてみましょう。いくつかのことを憶えておいてください。
- パターンについては1文字づつ考えます
- StringRegExp()がパターンの1文字目を見つけた後、それが本当に一致しているのかしていないのかの判定について指定するのはあなたの仕事です。例6はそれをよく表しています。
- [ ... ]はORを意味します([xyz] は"x"OR"y"OR"z"を意味します)
他に疑問点がある場合はまず最初にヘルプファイルを調べてください。SREの重要な構文についての詳細な説明があります。特に見ておく必要があるのは"繰り返し文字"についての章です。範囲指定を特定の文字で表すことでパターンがより読みやすくなります。例えば"*"は {0,}、つまり0以上の任意文字数と同等の意味を持ちます。

頑張って。正規表現はあなたのコードの量を大幅に減らしてくれ、後で修正することを容易にしてくれます。訂正とフィードバックは大歓迎です!

リソース


Wikipediaの記事 - Regular Expressions - blindwigに感謝。

The 30 Minute Regex Tutorial - 著者:Jim Hollenhorst


さまざまなStringRegExp()のパターンのテスト用GUI - steve8tchに感謝。クレジット:w0uter

 

 

 

このチュートリアルに関してneogiaに感謝。