🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の Regex — RegExp オブジェクトと、一致が空で返る理由

|

Excel VBA の Regex — RegExp オブジェクトと、一致が空で返る理由

TL;DR — VBA に Regex キーワードはありません。VBScript RegExp オブジェクト を作ります。 CreateObject("VBScript.RegExp") による遅延バインディングか、参照設定による事前バインディングの どちらかです。プロパティを設定するまで何もしません。.Pattern、そして 役に立たない ほうの値が 既定になっている 2 つ — .Global = False(最初の一致だけ)と .IgnoreCase = False(大文字小文字を 区別)です。それから正しい動詞を選びます。.Test は Boolean を返し、.Replace は文字列を返し (後方参照は \1 ではなく $1)、.Execute は一致のコレクションを返します。そのキャプチャした グループは .Value ではなく .SubMatches に住んでいます。

Sub RegexBasics()
    Dim re As Object
    Set re = CreateObject("VBScript.RegExp")   ' 遅延バインディング - 参照設定は不要
    re.Pattern = "PO-(\d+)"                     ' グループが数字をキャプチャする
    re.Global = True                            ' 最初の 1 件だけでなく、すべての一致を見つける
    re.IgnoreCase = True                        ' 大文字小文字を区別しない

    Dim m As Object
    For Each m In re.Execute("po-17 and PO-9004")
        Debug.Print m.Value             ' PO-17    次に PO-9004  (一致全体)
        Debug.Print m.SubMatches(0)     ' 17       次に 9004     (キャプチャしたグループ)
    Next m
End Sub

この記事で学べること

  • 考え方の軸 — regex はキーワードではなく、自分で作るオブジェクト
  • 遅延バインディング対参照設定と、そのあとに続くコンパイルエラー
  • Global = False が、最初の一致しか取れないことを意味する理由
  • TestReplaceExecute がそれぞれ何を返すか
  • 値を取り出すには Match.Value ではなく SubMatches へ手を伸ばす理由
  • VBScript のパターンフレーバーが対応するものと、しないもの

考え方の軸:regex はキーワードではなく、自分で作るオブジェクト

ほかのたいていの言語から来ると、regex の演算子か関数を期待します。VBA にはそのどちらもありません。 あるのは COM コンポーネント — VBScript Regular Expressions ライブラリ — で、これをインスタンス化し、 プロパティで配線し、それから呼び出します。だから考え方の軸は「関数を呼ぶ」ではなく「オブジェクトを 構成し、それから問いを投げる」です。以下のいらだちはすべて、実のところ「尋ねる前にプロパティを 設定し忘れた」なのです。

それはまた、いつ手を伸ばす価値があるかも教えてくれます。RegExp は、はしごのてっぺんにいる重量級で、 Like 演算子やただの InStr の上にいます。準備のコストを払い — 作って、パターンを設定し、スイッチを切り替える — その見返りに、 繰り返し回数、選択、そしてほかにはできない唯一のこと、キャプチャした断片を取り出す ことが手に 入ります。

準備:遅延バインディング対参照設定

オブジェクトを得る方法は 2 つあり、その選択には本当の結果が伴います。

' 遅延バインディング - 移植性があり、参照設定は不要、実行時に解決される
Dim re As Object
Set re = CreateObject("VBScript.RegExp")
' 事前バインディング - 先に参照設定を加えれば、これがコンパイルできる
' Tools -> References -> "Microsoft VBScript Regular Expressions 5.5"
Dim re As New RegExp

事前バインディングは IntelliSense と、わずかに速い呼び出しをもたらしますが、参照設定にチェックが入って いるときにしかコンパイルできません — そして、参照設定の欠けた相手にブックを送れば、相手は 1 文も実行 されないうちに As New RegExp の行で User-defined type not defined というコンパイルエラーを受け 取ります。遅延バインディング(As Object + CreateObject)には、そんなもろさは一切ありません。実行時に 解決され、参照設定なしで旅ができます。配布するものには遅延バインディングを選びましょう — 移植性は IntelliSense を失う価値があります。

罠 1:Global が off なので、最初の 1 件しか取れない

.Global は既定で False であり、そのたった 1 つの既定が、最もよくある「regex が壊れている」報告の 2 つを引き起こします。

re.Pattern = "\d+"
re.Global = False                       ' 既定
Debug.Print re.Replace("a1 b2 c3", "#") ' "a# b2 c3"  <- 最初の並びだけ置換された
Debug.Print re.Execute("a1 b2 c3").Count ' 1          <- 最初の一致だけ見つかった

re.Global = True
Debug.Print re.Replace("a1 b2 c3", "#") ' "a# b# c#"  <- 今度は全部
Debug.Print re.Execute("a1 b2 c3").Count ' 3

置換が最初の出現だけに触れた、あるいは 3 つ見えているのに検索が 1 件しか見つけなかった — これはほぼ 必ずこれが原因です。「あらゆる場所で」を意味するときは .Global = True を設定しましょう。その隣人 .IgnoreCase も同じように既定で False なので、明らかに違う大文字小文字のテキストに一致すべき パターンが一致しないなら、たいていは re.IgnoreCase = True の書き忘れです。

罠 2:Test・Replace・Execute は 3 つの異なるものを返す

このオブジェクトは 3 つの動詞を公開していて、その戻り値の型を混同するのが 2 つめの大きなつまずきです。

  • re.Test(s)Boolean を返します — パターンはどこかに一致するか? 検証として使います。 Like の正規表現版のいとこです。
  • re.Replace(s, replacement)String を返します — 各一致(Global なら)を置換した入力です。 置換文字列の中の後方参照は ドル記号 を使います。$1 が最初のキャプチャグループです。\1 と 書くとリテラルのバックスラッシュ 1 が挿入され、ほかの言語から来た人がよくやるバグです。
  • re.Execute(s)MatchCollection を返します — 調べたり取り出したりするためにループで回す Match オブジェクトのコレクションです。
re.Pattern = "(\d{4})-(\d{2})"
Debug.Print re.Test("2026-09")                 ' True
Debug.Print re.Replace("2026-09", "$2/$1")     ' "09/2026"  - $1、$2 がグループ

必要な答えで動詞を選びましょう。Yes/No なら Test、変換した文字列なら Replace、一致したテキストや その断片が欲しいなら Execute です。

罠 3:取り出すには Value ではなく SubMatches へ手を伸ばす

これが、人をぐるぐる回らせるやつです。.Execute から得る各 Match は、.Value — 一致した 全体 の テキスト — と、.SubMatches コレクション — かっこでくくった キャプチャグループ、あなたが取り出したい 部分 — を持っています。

re.Pattern = "Invoice #(\d+) dated (\d{4}-\d{2}-\d{2})"
re.Global = True
Dim m As Object
For Each m In re.Execute("Invoice #4471 dated 2026-09-04")
    Debug.Print m.Value          ' Invoice #4471 dated 2026-09-04   (一致全体)
    Debug.Print m.SubMatches(0)  ' 4471                              (最初のグループ)
    Debug.Print m.SubMatches(1)  ' 2026-09-04                        (2 番目のグループ)
    Debug.Print m.FirstIndex     ' 0    (入力内の 0 始まりの開始位置)
Next m

その数 が欲しいなら、欲しいのは m.Value ではなく m.SubMatches(0) です — .Value を読んで、なぜ まだ周囲のテキストが入っているのかと首をかしげるのが、取り出しのバグの第 1 位です。SubMatches は 0 始まりで、一致に参加しなかったグループは空文字列で返るので、インデックスで取りにいく前に re.Test(s) (かコレクションの .Count)でガードしましょう。

VBScript フレーバー:パターン言語が対応するものと、しないもの

VBA の正規表現は VBScript(RegExp 5.5) フレーバーであって PCRE ではなく、その隔たりが、ほかの どこかで正規表現の経験を積んだ人をつまずかせます。日常の道具立てには対応しています。\d \w \s と その否定、量指定子 * + ? {n} {n,m}、選択 a|b、グループ ( )、文字クラス [ ]、そしてアンカー ^ $(それらが行を意味するか文字列全体を意味するかは .MultiLine が決めます)。対応して いない のは、 後読み、名前付きキャプチャグループ、そしてアトミック/強欲でないグループです — それらに頼るパターンは、 あなたの期待どおりにはコンパイルされません。

VBA 固有の救いが 1 つ。VBA の文字列リテラルの中ではバックスラッシュがエスケープ文字で ない ので、 パターンはそのまま書けます — "\\d{4}" ではなく "\d{4}"。C#、Java、JSON とは違い、二重エスケープは 不要です。唯一二重にする必要があるのはリテラルの引用符で、それは VBA 自身の "" です。

意見:regex が儀式に見合うのは、本物の構造があるときだけ

RegExp は VBA で最も強力なテキスト道具であり、最も早まって手を伸ばされがちな道具でもあります。 オブジェクトを作り、パターンを設定し、GlobalIgnoreCase を忘れずに設定するのは、れっきとした 儀式です。固定の形やただの Yes/No には、それは要らない儀式です — Like は 読みやすい 1 行ですし、InStr は「このリテラルを 含む」に対してより速いのです。

線を引くべきところは、引き剥がさなければならない構造 です。パターンに可変の繰り返し、本物の選択、 あるいは — これが決め手 — 取り出さなければならないグループ があるとき、regex はそれをやってのける 箱の中の唯一の道具であり、そのぶんの働きをします。件名から注文番号を、ぐちゃぐちゃのタイムスタンプの 部品を、テキストの塊の中のすべてのメールアドレスを。そういうときには手を伸ばし、旅ができるように遅延 バインディングを選び、GlobalIgnoreCase を意図して設定し、断片は SubMatches から読みましょう。 それに届かないところでは、はしごを降りるのです。

解析こそが目的のとき — 仕事の方を説明する

regex が正しい道具のときでさえ、パターンが目的であることはめったにありません — 「この列からすべての 請求書番号とその日付を取り出して、2 つのフィールドに分けて」が目的です。そして、キャプチャグループを 持つ VBScript のパターン、Execute のループ、SubMatches の帳簿づけ、そして行がまったく一致しない 端のケース、これらは 1 つの意図のまわりの、たくさんのもろい機械仕掛けです。 ExcelMaster なら、結果を普通の 言葉で — 「B 列から請求書番号と日付を C 列と D 列に取り出して、一致のない行に印を付けて」 — と説明 できます。そして、本物の正規表現エンジンで取り出しを行い、まずファイルをバックアップし、解析できな かった行を報告する Python を生成します。あなたは何を取り出すかを説明するだけ。パターンも、グループも、 取りこぼしも、それが処理します。

よくある質問

VBA で正規表現を使うには?

VBScript RegExp オブジェクトを作り、そのプロパティを設定し、それからメソッドを呼びます。移植性のある やり方は遅延バインディングです。Set re = CreateObject("VBScript.RegExp")、それから re.Pattern = "\d+"re.Global = Truere.IgnoreCase = True、そして最後に、Boolean・新しい文字列・一致のどれが欲しいかに 応じて re.Test(s)re.Replace(s, repl)re.Execute(s) を呼びます。

VBA の正規表現が最初の一致しか置換しないのはなぜですか?

.Global が既定で False だからです。Global が off だと、.Replace.Execute も最初の一致で 止まります。文字列内のすべての一致に作用させるには、呼ぶ前に re.Global = True を設定します。

VBA の正規表現でキャプチャグループを取り出すには?

re.Execute(s) をループで回し、Match.SubMatches を読みます。SubMatches(0) が最初のかっこのグループ、 SubMatches(1) が 2 番目、と続きます(0 始まり)。Match.Value はグループではなく 一致全体 の テキストです — グループが欲しかったのに .Value を読むのが、取り出しの最もよくある間違いです。

VBA で正規表現を使うのに参照設定は必要ですか?

事前バインディングのときだけです。Dim re As New RegExp は Tools -> References の「Microsoft VBScript Regular Expressions 5.5」を必要とし、それが欠けていると User-defined type not defined でコンパイルに 失敗します。遅延バインディング — CreateObject("VBScript.RegExp") を伴う Dim re As Object — は参照 設定が不要で、共有するブックにはより良い選択です。

VBA はどの正規表現フレーバーを使い、後読みに対応していますか?

VBA は VBScript(RegExp 5.5)フレーバーを使います。\d \w \s、量指定子、選択、グループ、文字クラス、 ^ $ アンカー、そして $1 による置換の後方参照に対応しています。後読みや名前付きキャプチャグループには 対応していません。また、VBA の文字列リテラルの中ではバックスラッシュが特別でないので、二重エスケープ なしで "\d{4}" とそのまま書けます。

検証環境

検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-09-04.

関連ガイド: VBA Like · VBA IsNumeric · VBA InStr · VBA Replace · VBA Split