正規表現(Regular Expression、regex)は文字列のパターンを表現するための文字列です。
正規表現を使用すると文字列同士の単純な比較だけではなく、複雑なパターンに基づいた文字列の抽出や検索などの処理を効率的に行うことができます。
ある文字列が、与えられた正規表現のパターンに従っていることを、「文字列がパターンにマッチする」といいます。
ほとんどの人にとっては見慣れない表現になるため難しく感じてしまいがちですが、いくつかのルールを覚えれば正規表現を理解して使うことができるようになります。
以下の説明では正規表現のパターンや文字列を表す場合に""で囲っていますので注意してください。
正規表現を扱うVBAプログラム
以下は対象の文字列に正規表現のパターンにマッチする文字列が含まれるかを判定するコードです。
regex.Patternに正規表現のパターン、targetに判定したい文字列をセットして実行すれば、文字列がパターンにマッチするか否かを判定することができます。
この例では"....。?"というパターンに対して"おはよう"はマッチすると判定されます。
このパターンは「何らかの文字が4文字連続し、その後に"。"があってもなくてもよい」というパターンを表しています。
"おはよう"に"。"を付加した"おはよう。"でもマッチしますが、"fghjkl"や"20241231"といった文字列でも(要するに4文字以上の文字列であればなんでも)パターンに一致する部分を含むのでマッチします。
この後で正規表現のルールについて説明しますので、パターンと対象の文字列を書き換えて実際に試してみてください。すぐに感覚がつかめるはずです。
Sub regextest()
Dim regex As Object '正規表現オブジェクトを宣言
Set regex = CreateObject("VBScript.RegExp") '正規表現オブジェクトの作成
regex.Pattern = "....。?" 'パターン文字列を設定
Dim target As String '対象の文字列を宣言
target = "おはよう" '対象の文字列を設定
If regex.test(target) Then 'targetがパターンにマッチするか判定(test)する
MsgBox "マッチしました"
Else
MsgBox "マッチしませんでした"
End If
End Sub
正規表現のルール
正規表現はパターンを文字列で表現しますが、この表現に使われる特別な意味を持つ文字をメタ文字と呼びます。
メタ文字の組み合わせにより、複雑な条件を含むパターンでも一つの文字列で表すことができます。
"."(ピリオド)
"."は任意の1文字を表します。
例えば、"は.."という正規表現は"は"という文字の後に何らかの文字が2個連続する3文字のパターンを表し、"おはよう"の"はよう"の部分や"明日は晴れ"の"は晴れ"の部分がマッチします。
"*"(アスタリスク)
"*"は直前の文字が0回以上連続することを表現します。
例えば、".*"という正規表現は「何らかの文字」が0回以上連続することを表し、""(空の文字列)を含む全ての文字列にマッチします。
"あ*"という正規表現は"あ"が0回以上連続するパターンを表すため、".*"と同様に結果的に全ての文字列にマッチしてしまいます。
"+"(プラス)
"+"は直前の文字が1回以上連続することを表現します。
例えば、"20+倍"という正規表現は"200倍"や"2000倍"マッチしますが"201倍"や"2倍"にはマッチしません。
"?"(クエスチョン)
"?"は直前の文字が0個または1個であることを表現します。
".+です。?"という正規表現は何らかの文字列の後に"です"があれば、"。"があってもなくてもマッチします。
"|"(パイプ)
"|"で区切られた前後のパターンのいずれかであることを表現しています。
"今日|明日"という正規表現は"今日"か"明日"にマッチします。"|"を使用することで、複数の選択肢を持つパターンを表現することができます。
"[ ]"(角括弧)
"[ ]"の任意の1文字を表現します。
"[ACE]"であれば"A"、"C"、"E"のいずれかの文字にマッチします。つまり、対象の文字列がこれらの文字のいずれかを含んでいれば、パターンにマッチすることになります。
"-"(ハイフン)
"-"は"[]"内で使われることで範囲指定を表現することができます。
例えば"[A-Z]"はAからZまでの大文字アルファベット1文字のパターンを表し、"0-5"は0から5の範囲の半角数字1文字のパターンを表します。
"あ-お"のようにひらがな等の範囲を指定することも可能ですが、数字やアルファベットのように順序が明確なもの以外の範囲を指定する際には注意が必要です。
正確な結果を得るためには、対象の文字コードの順序やUnicode の範囲を考慮する必要があります。
"$"(ドル)
"$"は文末であることを表します。
例えば"暑い"という正規表現は文字列内に"暑い"を含んでいればマッチしますが、"暑い$"だと"暑いな"や"暑い。"のように"暑い"が文末でない文字列は除外されます。
"^"(キャレット)
"^"は文の先頭であることを表します。
例えば"^寒い"だと"寒いな"にはマッチしますが"ちょっと寒いな"にはマッチしません。
"$"と組み合わせて"^寒い$"とすれば、先頭と文末の文字列が"寒い"であるパターンを表すため、"寒い"以外の文字列にはマッチしません。
また、"^"は"[]"の中ではだと否定を表し、例えば"[^a]"ならa以外の文字、"[^0-9]なら数字以外の文字にマッチします。
"{ }"(中括弧)
"{ }"は直前の要素の繰り返し回数を指定するために使用されます。具体的には以下のように使われます。
"{n}"は直前の要素がちょうどn回繰り返されることを表します。
"{n,}"は直前の要素が少なくともn回以上繰り返されることを表します。
"{n,m}"は直前の要素がn回以上m回以下の範囲で繰り返されることを表します。
例えば、"a{2,5}"は、直前の文字"a"が2回以上5回以下の範囲で繰り返されるパターンを表し、"aa"や"aaa"、"aaaa"、"aaaaa"にマッチします。
"( )"(丸括弧)
"( )"はグループ化を表現します。
例えば"(hello)+"は"hello"という文字のグループが1回以上連続することを表します。
"(hello|goodbye)+"は"hello"または"goodbye"という文字列にマッチします。
正規表現において"( )"はキャプチャ、バックレファレンスという機能も持っていますが、これらの機能はより複雑なパターンやデータの抽出に使用され、説明が複雑になるためここでは省略します。
"\"(バックスラッシュ)
"\"はエスケープ文字を意味します。正規表現に使われるメタ文字をそのままの文字として表現したい場合に使われます。
例えば、"end."という文字列にマッチすることを期待して"end."というパターンを作った場合、"."が任意の一文字を表すメタ文字として解釈されるため、"endq"などの本来の意図と異なる文字列もマッチしてしまいます。
ここで正規表現を"end\."とすることで、"\"の直後の"."が"."として解釈されるので、"end."以外の文字列はマッチしなくなります。
なおバックスラッシュと書いているように、正規表現における本来の記号はバックスラッシュですが、VBEなどバックスラッシュが入力、表示できないエディタで正規表現を記述する場合、"円記号"が使われるのが一般的です。
正規表現とInStr()関数
ここまで扱ってきたregex.test関数は、パターンにマッチする部分文字列が対象の文字列に含まれるかを判定します。
具体的にどの部分がマッチしたのかを知りたい場合、regex.Execute関数を使うことでマッチした部分文字列の情報を知ることができます。
ただし、パターン=特定の文字列が対象の文字列に含まれているかを調べるような単純な検索の場合、正規表現よりもInStr()関数を使用した方が簡潔です。
例えば、"I have an apple and a pen."という文字列に"apple"というパターンが含まれているかを判定したい場合、以下のようにInStr()関数を使えば十分です。
Sub instrtest()
If InStr("I have an apple and a pen.", "apple") > 0 Then
MsgBox "マッチしました"
Else
MsgBox "マッチしませんでした。"
End If
End Sub
InStr関数は検索文字列が対象の文字列の中に見つかった時、先頭の文字の位置を返す関数です。
正規表現は、このような単純な一致ではなく複雑なパターンと文字列を比較したい場合に役立ちます。
実際に正規表現の使いどころを見つけるのが難しいかもしれませんが、必要に迫られたときに正規表現を知っていると便利です。
もしInStr()関数では書きづらい複雑な状況に直面したとき、ここで紹介した正規表現を思い出して活用してみてください。