StringRegExp()の謎を解き明かすちょっとしたガイド。
StringRegExp(
"test",
"pattern" [,
flag ] )
"test" = 検索対象の文字列。
"pattern" = 特定の文字キーからなる文字列で関数に正確にあなたが検索したいものを知らせます。もしも、かつ、ただし...はあり得ません。一致するか、それともしないかです。
flag[オプション] = "pattern"が見つかったことのみを通知するのか、最初の一致ヵ所を返すのか、"test"文字列内の全ての一致ヵ所返すのかを関数に設定します。
例 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}'))
$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以上の任意文字数と同等の意味を持ちます。
頑張って。正規表現はあなたのコードの量を大幅に減らしてくれ、後で修正することを容易にしてくれます。訂正とフィードバックは大歓迎です!
The 30 Minute Regex Tutorial - 著者:Jim Hollenhorst
さまざまなStringRegExp()のパターンのテスト用GUI - steve8tchに感謝。クレジット:w0uter
このチュートリアルに関してneogiaに感謝。