文字列の受理― ワイルドカード・Like・正規表現を理解する

文字列の受理― ワイルドカード・Like・正規表現を理解する

~~~「一致する/取り出す/置換する」を軸に、グルーピング・SubMatches・ゼロ幅マッチまで最短でつなぐ~~~

Copyright © 2025 LWP 山中 一弘 本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。

要約

本記事(講義)は、VBAで文字列を扱う際に避けて通れない「文字列の受理(=入力文字列が条件を満たすかを判定し、必要なら取り出し・変換する)」を、ワイルドカード/Like 演算子/正規表現の3段階で整理する。

まず「解析器(受理器)の基本」として、マッチとは何かを定義する。ここで、受理(全体一致)と検索(部分一致)、および「判定/抽出/置換」の3タスクを同じ見取り図に置く。以降は、ワイルドカード、Like、正規表現の順に、どのように入力を読み(トークン化し)、消費し、受理に到達するかを押さえる。正規表現は Match/SubMatches/ゼロ幅マッチまで扱い、regTest・regExtract・regReplace のログ出力で挙動を“見える化”して腹落ちさせる。理論(NFA/DFAなど)には踏み込みすぎず、1時間で「パターンの動きを頭の中で再生できる」状態を目指す。

1 解析器の基本:マッチとは何か

この章では、ワイルドカード/Like 演算子/正規表現を「同じ土俵」で理解するために、解析器(受理器)の共通モデルを作る。ここでいう解析器は、言語処理の厳密なパーサというより、入力文字列をルールに沿って読んで「受理できるか」を判定し、必要なら中身を取り出し、置換で変形するための“読み取り機構”である。

以降の章は道具が変わるだけで、やることは同じである。入力を読む。ルールに沿って消費する。成功なら受理、失敗なら不受理。抽出や置換は、受理の途中で得られる情報を使って結果を作る。まずは、この共通の骨格を頭に入れる。

1.1 文字列の受理(全体一致)と検索(部分一致)を最初に分ける

最初につまずきやすいのは、「一致」と言ったときに何を意味しているかが混ざることである。ここでは、受理(全体一致)と検索(部分一致)を、別物として固定する。

受理(全体一致)とは、「入力文字列の先頭から末尾まで」を、パターンが完全に説明できた状態である。言い換えると、パターンが入力を最後まで消費し切って、受理状態で終わることである。ワイルドカードや Like はこの思想が強く、基本動作は全体一致である。

検索(部分一致)とは、「入力のどこかに」パターンに合う部分が存在する状態である。正規表現の test や exec は、設定次第で検索として動かせる。ワイルドカードや Like でも部分一致はできるが、それは「pattern のように書いて、全体一致の中に検索を埋め込む」ことで実現する。

この区別を曖昧にしたまま例を見ると、「同じパターンなのに True になったり False になったりする」ように感じる。実際には、判定対象が「全体」なのか「部分」なのかが違うだけである。以降の説明では、常にこの二つを分けて扱う。

1.2 マッチの定義:一致は「文字」ではなく「規則に沿った消費」である

マッチを「文字が同じかどうか」と捉えると、すぐに破綻する。* や ? や [ ] が入った瞬間に、「同じ」とは何かが曖昧になるからである。ここでは、マッチを次のように定義する。

マッチとは、入力を左から読み進め、パターンが要求する規則に従って入力を消費し、最後に受理状態に到達することである。

この定義を採用すると、次のことが自然に説明できる。

第一に、マッチには「位置」がある。いま入力の何文字目を見ているかが状態に含まれる。ゼロ幅マッチは、この「位置」にだけ作用し、文字を消費しない特殊な規則である。

第二に、マッチには「選択」がある。* のように「0文字でもよいし、もっと食ってもよい」という規則は、複数の消費パターンを生む。このため、探索(戻り)が必要になる。

第三に、成功は一度では決まらない。いったん進んだ消費の選択が、後段で失敗の原因になることがある。そのときは分岐点まで戻り、別の消費を試す。この現象がバックトラックである。

このように、マッチは「等号で比較する」処理ではなく、「規則に沿って入力を読み、進み、必要なら戻る」処理である。ここを押さえると、道具が変わっても同じ直感で追える。

1.3 今日使う3つのタスク:判定/抽出/置換

文字列処理は、最終的に次の三つに分解できる。

判定は、「この入力は条件を満たすか」を True/False で返す。入力チェック、ファイル名検査、形式の妥当性確認などはここに入る。ワイルドカードや Like は、判定用途に強い。

抽出は、「条件を満たすなら、必要な部分を取り出す」である。日付やIDを抜く、括弧内だけ取る、CSVの一部を拾うなどが典型である。抽出が必要になった時点で、Like だけでは苦しくなり、正規表現が有力になる。

置換は、「条件に合う部分を別の形に変える」である。整形、正規化、不要語の除去、区切り文字の統一などが該当する。正規表現は、抽出と置換を同じ枠組みで扱える点が強い。

重要なのは、三つは別々の技能ではなく、同じ解析器モデルの“使い分け”であることだ。判定は「受理できるか」を見る。抽出は「受理の過程で得られる情報」を取り出す。置換は「受理の結果を使って出力を組み立てる」。この対応を頭に置けば、道具の選択がブレなくなる。

1.4 解析器(受理器)の見取り図:トークン化→消費→受理状態

解析器(受理器)は、概念として次の三段で動く。

第一段はトークン化である。パターンを、意味を持つ部品に分解する。たとえば、通常文字(LIT)、任意1文字(?)、任意列(*)、文字集合([A-Z])などである。Like とワイルドカードはこの段が分かりやすい。正規表現はこれに加えて、繰り返し、選択、グルーピングなどが入る。

第二段は消費である。入力の現在位置から、トークンが要求する規則で文字を消費し、入力位置を進める。LIT なら一致する1文字を消費する。? なら任意の1文字を消費する。* なら0文字以上を消費できるため、ここで分岐が生まれる。

第三段は受理状態である。入力位置が末尾に到達し、かつパターンも最後まで処理できたとき、受理となる。ここで受理(全体一致)が定義できる。検索(部分一致)は、入力の開始位置をずらしながら受理を試す、またはパターン側に「前後を何でもよい」にするトークンを置いて実現する。

この見取り図を持っていれば、道具が違っても観察すべき点が同じになる。パターンがどうトークン化されるか。どこで分岐が生まれるか。入力を消費し切る条件は何か。これだけで挙動の半分は説明できる。

1.5 戻るという現象:貪欲・バックトラックを“動作”として捉える

分岐があると、解析器は「どのくらい消費するか」を選ばなければならない。ここで出てくるのが貪欲とバックトラックである。

貪欲とは、まずは大きく食う、という方針である。* が出たら可能な限り多く消費して先へ進む。これ自体は合理的で、成功するなら最短で終わる。しかし、後段のトークンが合わない場合がある。

そのときに起きるのがバックトラックである。解析器は、直前の分岐点に戻り、消費量を一段減らして再試行する。これを繰り返して、どこかで全体が成功する消費パターンを探す。つまり、バックトラックは「戻ってやり直す」動作であり、探索である。

ここで注意点が二つある。

一つ目は、「戻る場所」が限定されることだ。戻れるのは、選択肢があった場所だけである。* や選択(OR)や繰り返しが、それに当たる。

二つ目は、戻りが増えると遅くなることだ。特に、曖昧な繰り返しと選択が重なると、試す経路が爆発し得る。ここでは理論に深入りしないが、「分岐が多いほど探索が増える」という直感だけは持っておくとよい。

この章では、バックトラックを「難しい概念」ではなく「動作」として扱う。線路を進んで、分岐で失敗したら分岐点まで戻り、別ルートを試す。こう考えると、ワイルドカードでも Like でも正規表現でも、同じ絵で説明できる。

1.6 可視化の道具:regTest・regExtract・regReplaceで何を読むか

最後に、講義で使う可視化の道具を定義する。目的は「正しいかどうか」ではなく、「何が起きたか」を観察できるようにすることである。

regTest は判定の道具である。入力とパターンを与えたとき、受理できたかを True/False で返す。ここで読むべきは、受理と検索のどちらで評価しているか、そして境界条件である。たとえば、全体一致で False でも、部分一致なら True になり得る。

regExtract は抽出の道具である。Match の開始位置、長さ、値を出し、さらに SubMatches を表示する。ここで読むべきは三点である。どこからどこまでがマッチしたか。グループが何を捕まえたか。ゼロ幅のときに長さが0になるか。これが見えれば、「丸かっこの役割」や「位置にマッチする」感覚が固まる。

regReplace は置換の道具である。置換後の文字列だけでなく、どの部分がどの規則で置き換えられたかをログで追う。ここで読むべきは、「何を抽出して、どう再構成したか」である。$1 などの参照が、抽出と置換をつなぐ橋になる。

この三つを揃えると、文字列処理は一気に扱いやすくなる。判定は最小。抽出で中身が見える。置換で成果物が出る。以降の章は、この三点セットで同じ題材を回しながら、道具の違いを腹落ちさせる。

2 ワイルドカード:最小のパターンマッチ

ワイルドカードは、文字列パターンの入口として最適である。理由は二つある。記法が少なく、直感で読めること。もう一つは、Like や正規表現へ進む前に「受理=入力を消費して終わる」という発想を身体化できることである。

ここでは、ワイルドカードを「最小の受理器」として扱う。どの記号が何を意味するかだけでなく、解析器の内部で何が起きているかを、トークン化と消費の観点で説明する。最後に、Windows のコマンドプロンプトで使えるワイルドカード(*.bas など)を取り上げ、日常の作業で“手触り”を作る。

2.1 ワイルドカードでできること/できないこと

まず、ワイルドカードでできることは「簡単な形の判定」である。代表は、拡張子やファイル名の形、固定の接頭辞・接尾辞、特定位置の1文字だけ自由、といった条件である。

できることの例を挙げる。

・拡張子で絞る(.bas、.csv、.xlsx など)

・接頭辞で絞る(SGKZ_ のような命名規則)

・接尾辞で絞る(*_test.bas のような規約)

・一定位置の1文字だけ任意(file?.csv など)

一方で、できないことも早めに固定しておく。

第一に、取り出し(抽出)はできない。たとえば「ファイル名の中の年月だけ取りたい」という要求は、ワイルドカードの守備範囲外である。

第二に、繰り返し回数の制約や高度な条件は書けない。たとえば「数字が3桁」や「英字+数字の組み合わせ」などは、ワイルドカードの表現力を越える。

第三に、複数条件の組み合わせが苦手である。AND/OR を混ぜて、少し複雑な条件を書くと、すぐに読めなくなる。読めないパターンは保守できないので、その時点で Like や正規表現に移るのが正しい。

ワイルドカードは、あくまで「最小の判定器」である。判定以外を欲張らないのがコツである。

2.2 受理の条件:入力を全消費できたら True になる

ワイルドカードの動作は、受理(全体一致)として捉えると整理が一気に楽になる。

パターンが入力を左から順に読み、規則に従って入力を消費し、入力の末尾まで到達したときに True になる。逆に、どこかで規則が成立しない、または入力が余ってしまうと False になる。

この「入力が余ると False」が重要である。たとえば、abc* は abc で始まる任意列を許すので、abc も abcd も受理する。しかし、*abc は末尾が abc で終わる必要があるので、xabc は受理しても abcX は受理しない。

この見方を固定すると、「部分一致をしたい」ときに何をすべきかも自動的に決まる。ワイルドカードは仕様として検索(部分一致)を持たない。したがって、部分一致をしたいなら「前後を何でもよい」にする記号を置いて、全体一致の形に落とす必要がある。

2.3 ワイルドカードの解析器(概念)

ここからは、内部で何が起きているかを、解析器(受理器)の見取り図で説明する。ポイントは、パターンをトークン化して、そのトークンが入力を消費していくということだけである。厳密な理論に踏み込む必要はない。

2.3.1 パターンをトークン列にする(LIT/?/*/文字集合)

ワイルドカードのパターンは、次のようなトークン列として扱える。

LIT:通常文字の並び。たとえば abc は LIT(a) LIT(b) LIT(c) である。入力側の同じ位置に同じ文字が必要になる。

?:任意の1文字を消費する。入力が1文字分進む。

:任意の0文字以上を消費する。ここが分岐点になる。

文字集合:環境によっては [a-z] のような集合を持つことがあるが、コマンドプロンプトの一般的なワイルドカードとしては基本の外に置くのが安全である。ここでは、ワイルドカードの最小核として LIT/?/ に集中する。

このトークン化ができると、パターンが何を要求しているかを「動作」として追える。たとえば file?.csv は、file の固定文字列を消費し、任意1文字を消費し、.csv を消費する、という手順に分解できる。

2.3.2 マッチャの2方式:DP(表)と貪欲+バックトラック

マッチャの内部実装は大きく二つに分かれる。ここは実装論なので深入りしないが、「なぜ戻りが起きるか」を理解するために最小限だけ触れる。

一つは DP(動的計画法)である。パターンの位置 i と入力の位置 j の組み合わせを表で管理し、「ここまで一致できるか」を埋めていく方式である。分岐があっても、表の遷移として処理できるため、安定した挙動になる。

もう一つは、貪欲+バックトラックである。* が出たらまず多めに消費して先へ進む。後で失敗したら、* の地点に戻って消費量を減らしてやり直す。人間が手で試行するときの発想に近い。

どちらも「分岐があるから探索が必要」という点は同じである。利用者側は、* の置き方が探索量を左右する、とだけ覚えておけばよい。

2.3.3 部分一致は仕様ではなく書き方(pattern)で作る

ワイルドカードは「検索」を持たない。したがって、部分一致をしたいなら、全体一致として受理できる形に書き換える。

基本形は、前後に * を付けることである。

pattern を「どこかに含む」にしたいなら pattern とする。

先頭一致なら pattern* とする。

末尾一致なら *pattern とする。

このルールは Like にもそのまま移植できる。さらに正規表現に進んだときも、「全体一致なのか部分一致なのか」を意識できるようになる。つまり、ワイルドカードでこの発想を固めておくことが、次章以降の地盤になる。

2.4 ありがちな誤解(正規表現と同じではない)

ワイルドカードは正規表現の簡易版ではない。似ているように見えるのは、* と ? が「曖昧さ」を表すからである。しかし、両者は目的と能力が違う。

第一に、ワイルドカードは基本がファイル名などの単純な判定であり、抽出を前提にしない。正規表現は、抽出と置換が中心にある。

第二に、ワイルドカードの記号は少なく、組み合わせの意味も限定される。正規表現は、繰り返し、選択、グルーピング、ゼロ幅など、表現力が桁違いである。

第三に、同じ見た目でも意味が違うことがある。特に、.(ドット)や + のように、正規表現では特別な意味を持つが、ワイルドカードではただの文字として扱われる場合が多い。この取り違えが、現場での事故につながる。

ワイルドカードは、最小の受理器として使い切る。複雑にしない。読める範囲で止める。それが最短ルートである。

2.5 コマンドプロンプトでのワイルドカード活用:一瞬で確認する

ここからは、Windows のコマンドプロンプトでの具体例である。ワイルドカードは、文字列処理を学ぶうえで最も身近な教材になる。ファイル一覧を一瞬で絞り込めるため、「受理=全体一致」「* の前後で意味が変わる」を体感できる。

2.5.1 *.bas で拡張子を絞る

カレントフォルダの bas ファイルだけ見たいなら、次で足りる。

dir *.bas

これは「末尾が .bas のものを受理する」パターンである。* は任意のファイル名部分を表し、.bas が末尾固定になる。

2.5.2 *一覧.csv のように接尾辞で絞る

末尾が「一覧.csv」で終わるファイルだけに絞るなら、次の形になる。

dir *一覧.csv

が前に付くことで、前半は何でもよく、末尾が固定であることだけを要求する。これが「末尾一致」の典型である。

2.5.3 先頭一致と部分一致を作る

接頭辞が固定なら、次のように書ける。

dir SGKZ_*.bas

「SGKZ_ で始まり、末尾が .bas のもの」を受理する。部分一致にしたいなら、前後に * を付ける。

dir SGKZ.*

ただしこの形はヒット範囲が広くなる。実務では、できるだけ「先頭一致」や「拡張子指定」を組み合わせて範囲を狭める方が安全である。

2.5.4 確認用の一覧CSVを作る

目視確認したいときは、dir の結果をファイルに出すとよい。最小構成は次である。

dir /b *.bas > bas一覧.txt

/b はファイル名だけの一覧にするオプションである。CSV が欲しければ、区切り文字を含む形に加工する方法もあるが、この段階では「まず一覧を作れる」だけで十分である。文字列の受理は、検証しやすい形に落とすのが勝ち筋である。

この章の要点は、ワイルドカードを「最小の受理器」として捉え、全体一致と * の挙動を体感することである。次章の Like は、同じ発想を VBA の中に持ち込んだものとして理解できる。

2.6 DPの動作概略

DPの図解では、最初に「1マスが何を意味するか」を言い切ってから先へ進むのが一番大切である。dp(i,j) は「テキスト先頭 i 文字」と「パターン先頭 j 文字」が一致可能なら True、という“到達可能性”の表である。ここで i と j は“文字の位置”ではなく“先頭から何文字を消費した状態か”を表す。図は、横軸にテキスト、縦軸にパターンを置けばよいが、必ず先頭に ε(空文字)を追加して (0,0) を作り、ここを基準点にする。

次に押さえるべきは、DP表が「1本の経路」を描くものではない点である。DP表は「到達できるマスの集合」を True として塗るもので、同じマスが True になる理由は複数あり得る。したがって、矢印や太線で“道筋”を描くのは説明としては有効だが、DP表そのものを道筋だと思うと誤解が起きる。DPの本体は、あくまで True が立っているマスの分布である。

初期条件は dp(0,0)=True である。さらに dp(0,j) は「空文字がパターン先頭 j 文字で受理できるか」を表し、パターン先頭が * の連続で空文字を作れる範囲だけ True になる。この初期化を入れると、* が「空でもよい」という性質を持つことが、表の左上の段階で可視化される。

表の埋め方自体は、単純な二重ループである。各マス dp(i,j) は、近傍3点(左 dp(i,j-1)、上 dp(i-1,j)、左上 dp(i-1,j-1))だけを見て True/False を決める。通常文字は左上だけを参照し、文字が一致するときに dp(i,j)=dp(i-1,j-1) が True になる。ワイルドカードの ? も任意1文字なので、やはり左上だけを見て dp(i,j)=dp(i-1,j-1) になる。したがって通常文字や ? は、True が右下斜めに連なっていく形で表に現れる。

横方向(右)や縦方向(下)に True を広げるのは * だけである。* のとき dp(i,j)=dp(i,j-1) OR dp(i-1,j) となる。左 dp(i,j-1) から True が来るのは「* を空文字として扱い、パターンだけ1つ進む」場合である。上 dp(i-1,j) から True が来るのは「* がテキストを1文字食い、パターンは * の位置に留まる」場合である。ここが理解できると、* の行や列で True が帯のように広がりやすい理由が説明でき、途中で不自然に途切れる図が誤りだと判断できる。

見た目としては「右・下・右下に True が伝播する」と捉えてもよいが、実装上は逆である。二重ループで dp(i,j) を埋めるときは、常に“過去”である左・上・左上を参照して「今」を決める。したがって図解でも「この True はどの近傍の True から来たのか」を意識して塗ると、受理の仕組みが崩れない。

最後に取り違えを防ぐ。ここで扱う任意1文字は ? であり、正規表現の . ではない。ドット記号はワイルドカードでは通常文字として扱い、テキスト側のドットにしか一致しない。この前提を守ると、*.csv の例では * が広い到達可能性を作り、その後に . c s v が斜めに一本化していく、という典型的なDPの形が素直に表に現れる。

3 Like 演算子:VBA標準の“軽量フィルタ”

Like 演算子は、VBAに最初から入っている「軽量なパターン判定」である。ワイルドカードと似た記号を持つが、目的はファイル検索ではなく、文字列が条件を満たすかを手軽に判定することにある。

この章では、Like を「受理器(全体一致が基本)」として理解し、どの場面で頼れて、どこから先で破綻するかを明確にする。正規表現へ行く前の“最後の手軽な一手”として、使いどころを固定するのが狙いである。

3.1 Like の用途と位置づけ(UI入力チェック/簡易分類)

Like の得意分野は、判定である。文字列が「その形式っぽいか」を素早く確認し、処理を分岐させる用途に向いている。代表は次の二つである。

一つ目は UI 入力チェックである。たとえば、入力欄に「日付っぽい形」「社員番号っぽい形」「郵便番号っぽい形」が入っているかを、簡単な規則で弾く。厳密な検証は別に持つとして、入口での粗いチェックに Like はちょうどよい。

二つ目は簡易分類である。ファイル名やシート名、コードのプロシージャ名などを、命名規則で分類する。たとえば *_test や SGKZ_#### のような規則を、条件分岐で書くときに読みやすい。

Like は「読みやすさ」と「速度」と「依存の少なさ」が武器である。正規表現を持ち込むほどでもないが、単純な InStr では不足する。そういう中間に Like がある。

ただし、Like は抽出ができない。ここが決定的な限界である。チェックできても「どの部分が何か」を取り出せないので、データ化が必要になった瞬間に正規表現へ移る判断が必要になる。

3.2 Like パターンの記法と注意点(?/*/#/[ ]/[! ])

Like の記号は少ない。少ないからこそ、運用ルールを決めておくと安定する。

? は任意の1文字である。固定長の形式チェックに向く。

例として、AA-???? のように「前半が固定、後半が4文字」といった形が書ける。

は任意の0文字以上である。前後の固定文字列と組み合わせて使う。

例として、"SGKZ_*" は接頭辞チェックである。

は任意の1桁の数字である。数字だけを要求したいときに便利で、Like の強みの一つである。

例として、"####-##-##" のように「数字の並び」を短く書ける。

[ ] は文字集合である。特定の文字範囲や文字リストを許可できる。

例として、"[A-Z]" は英大文字1文字、"[0-9]" は数字1文字、"[AB]" は A か B のどちらか、という意味になる。

[! ] は否定の文字集合である。集合に含まれない1文字を表す。

例として、"[!0-9]" は数字以外の1文字、という意味になる。

注意点は三つある。

第一に、Like は基本が全体一致である。部分一致をしたいときは、"*pattern*" のように前後に * を付ける設計になる。ここはワイルドカードの考え方と同じである。

第二に、パターンは「読みやすい範囲」で止める。記号が増えて読めなくなった時点で、Like を選んだ意味が消える。

第三に、環境依存の要素(大文字小文字の扱い)が混ざる。これは次節でまとめて扱う。

3.3 Like の解析器(概念)

Like を使いこなすコツは、「文字列比較」ではなく「受理器」として捉えることだ。パターンをトークン化し、入力を消費し、最後まで消費できたら True になる。このモデルで見ると、Like の挙動は安定して説明できる。

3.3.1 Like も受理器:全体一致がデフォルトである

Like の判定は、原則として全体一致である。パターンが入力の一部に当てはまっても、入力が余れば False になる。これを忘れると、期待と結果がズレる。

たとえば、"abc" Like "b" は False である。部分に b があっても、全体一致ではないからである。部分一致をしたいなら、"abc" Like "*b*" のように書いて、全体一致の形に落とす必要がある。

この「全体一致の設計」は、入力チェックに向く理由でもある。入口の判定で「余計な文字が混じっていないか」を同時にチェックできるからである。

3.3.2 文字比較ルール(Option Compare の影響を含む)

Like は、文字を比較する。したがって、文字比較のルールが結果に影響する。VBAでは、そのルールが Option Compare と、文字種(全角半角など)やロケールの影響を受けることがある。

実務で重要なのは、「英字の大文字小文字が一致扱いになるかどうか」である。ここは環境や指定によって変わり得るため、Like を入力チェックに使うなら、前提を固定しておく必要がある。

運用としては次の二択が現実的である。

・大文字小文字を区別したい場合は、事前に StrComp の比較モードや UCase$/LCase$ で正規化してから Like を当てる。

・区別しなくてよい場合でも、同じ正規化をかけておくと結果が安定する。

Like は「軽量」なぶん、比較ルールが暗黙に混ざると事故りやすい。ここを設計で潰すのが安全である。

3.3.3 * による分岐:直前の * に戻るという発想

Like の実装は公開仕様として保証されているわけではないが、挙動としてはワイルドカードと同様に「* が分岐点になる」と考えると理解しやすい。

は 0文字以上を許すので、「どこまで食うか」が複数あり得る。まず多めに食って先へ進み、後段が合わなければ戻って調整する。この戻りがバックトラックである。

ここで押さえるべきは、「戻る場所は * の地点である」ということだ。つまり、* を増やすほど分岐点が増え、試す経路が増える。Like は軽量だが、むやみに * を多用すると、パターンが読みにくくなるだけでなく、挙動も追いにくくなる。

実務では、* は原則として「前後どちらか一方」に寄せる。接頭辞チェックなら prefix*、接尾辞チェックなら *suffix、包含チェックなら *mid* の一回だけにする。この制限だけでも、Like の運用はかなり安定する。

3.4 Like が破綻する境界(複雑条件、抽出が要る、条件の合成が増える)

Like は便利だが、限界を越えると一気に辛くなる。破綻の境界は次の三つである。

第一に、複雑条件である。たとえば「英字2文字+数字4桁+任意文字列+末尾が特定語」のように、規則が長くなると、パターンが読めなくなる。読めないパターンは保守できない。

第二に、抽出が要る場合である。Like は True/False を返すだけで、どの部分が何に対応するかを返さない。社員番号から部署コードだけ取りたい、日付の年月日を分解したい、という要求が出た時点で Like では止まる。ここは迷わず正規表現に切り替える。

第三に、条件の合成が増える場合である。Like 自体は単一パターンの判定に向くが、AND/OR で複数パターンを組み始めると、判定ロジックが散らかる。分類ルールが増えるなら、正規表現に寄せて「抽出→分類」に変える方が設計としてきれいになる。(※複数のLike判定を作成して最後にブール演算すればある程度の対応は可能)

Like は「軽量フィルタ」である。入口で弾く、ざっくり分類する、読みやすい規則を短く書く。この範囲に留めると、最も強い。限界を越えたら、次章の正規表現へ進むのが最短である。

4 正規表現:受理・抽出・置換を一気通貫にする

正規表現(regular expression)は、文字列の集合を定義するための記法である。あるパターンに一致する文字列を「受理する(マッチする)」と捉えると、正規表現は「どの文字列を受理し、どの文字列を拒否するか」を、短い式として書ける仕組みである。言い換えると、正規表現は単なる検索用の道具ではなく、文字列の言語(language)を指定するための形式的な表現である。

正規表現の核は、連接・分岐・反復の三つの構成規則である。連接は「AのあとにBが続く」を表し、分岐は「AまたはB」を表し、反復は「Aを0回以上(または1回以上、0/1回)」のように繰り返しを表す。この三つの組み合わせで、受理する文字列の集合を段階的に組み立てていく。実務で使う括弧によるグルーピングは、これらの結合順序を明示するための記法だと考えると理解しやすい。

この「連接・分岐・反復だけで定義される」範囲の正規表現が表す言語は、理論上は有限オートマトンで受理判定できる。有限オートマトンとは、入力を左から読みながら、有限個の状態のあいだを遷移していく機械である。ある状態が受理状態になっていればマッチ成功、そうでなければ失敗、という形で判定できるため、ここで必要な「状態管理」は有限個で足りる。したがって、正規表現は「有限状態で解析できる文法クラス(正規言語)」だと言える。

一方、実務の「regexエンジン」は、理論上の正規表現に拡張を追加していることが多い。代表例が先読みなどのゼロ幅アサーションで、文字を消費せずに「この位置の先が条件を満たすか」を主張する機能である。先読み自体は多くの場合、受理集合の性質としては正規の範囲に収まるが、実装上はバックトラック(試して戻る)で評価されることがあり、動きが“文法解析っぽく”見える原因になる。

さらに強い拡張として後方参照(\1 など)を許す実装もある。後方参照は「以前にマッチした部分と同じ文字列を、後でもう一度要求する」機能であり、一般には有限状態では扱えない性質を持つ。つまり、世間で「正規表現」と呼ばれていても、機能セットによっては厳密な意味での正規表現(正規言語)から外れる場合がある。ここが、理論としての正規表現と、実装としてのregexの重要な違いである。

まとめると、正規表現は本来「連接・分岐・反復」で言語を組み立て、有限状態で受理判定できる形式である。実務ではこれに先読みなどの拡張が加わり、さらに後方参照のように正規言語の枠を超える機能まで含む場合がある。したがって「正規表現とは何か」を説明するときは、まず核となる文法クラス(正規言語)を押さえ、そのうえで実装の拡張がどこまで含まれているかを区別して述べるのが適切である。

ワイルドカードや Like が「受理できるか(True/False)」を中心にしているのに対し、正規表現は「受理した結果として、何がどこにマッチしたか」を構造化して返す。ここが決定的に違う。

VBA では、正規表現は VBScript.RegExp を使うのが一般的である。最初は記号が多く見えるが、理解の順序を誤らなければ難しくない。コツは「暗記」ではなく、「解析器の動作」を頭の中で再生できるようにすることだ。本章では、最小用語から入り、解析器イメージ、Match/SubMatches、置換、ゼロ幅の順に積み上げる。

4.1 正規表現に入る前の最小用語(マッチ/グループ/位置)

正規表現を使う前に、最低限の用語だけ固定する。ここを曖昧にすると、以降の例が全てぼやける。

マッチとは、入力のある範囲がパターンの規則に従って受理された結果である。ワイルドカードや Like では True/False だけだったが、正規表現では「どこからどこまでがマッチしたか」を持つ。

グループとは、パターンの一部を「ひとかたまり」として扱う構造である。丸かっこで作る。グループは、順序や繰り返しの単位にもなるし、取り出したい部分(キャプチャ)にもなる。ここが後で重要になる。

位置とは、文字ではなく「境界」である。文字と文字の間、先頭、末尾など、入力上の“地点”を指す。ゼロ幅マッチは、文字を消費せずに位置へマッチする。正規表現が強い理由の一つは、位置を条件にできることにある。

この三つを軸にすれば、ほとんどの挙動は説明できる。

4.2 正規表現の解析器イメージ(最小限):読む順序と戻り

正規表現の解析器も、基本は「トークン化→消費→受理状態」である。違いは、トークンの種類が多いことと、分岐が多いことだ。

読む順序は、原則として左から右である。パターンを読み、入力を消費しながら進む。ここまでは同じである。

戻りは、分岐がある場所で起きる。典型は次である。

・選択(A|B のように複数ルートがある)

・繰り返し(* や +、{m,n} のように回数が幅を持つ)

・曖昧な量指定(.* のように、どこまで食うかが複数あり得る)

解析器は、まずあるルートを選んで進み、後で行き詰まったら分岐点へ戻って別ルートを試す。この「進む→失敗→戻る→再試行」がバックトラックである。ここまでは Like の * と同じ発想でよい。

ただし、正規表現は分岐点が増えやすい。だからこそ、設計が重要になる。特に「.* を挟む」「選択を重ねる」「繰り返しの中に繰り返しを置く」などは、戻りが増える原因になりやすい。最初から複雑に書かず、小さく分けて検証するのが鉄則になる。

4.3 Match と SubMatches:結果が「オブジェクト」になる意味

正規表現が Like と決定的に違うのは、結果が True/False では終わらない点である。マッチ結果が「オブジェクト」として返ることで、受理の結果を次の処理へ渡せる。

Match は、少なくとも次の情報を持つ。

・どこからマッチしたか(開始位置)

・どれだけの長さか(長さ)

・マッチした文字列そのもの(値)

この三点が揃うと、処理が一気に組み立てやすくなる。たとえば、CSV から ID を抜くとき、ID が存在するか(判定)だけでなく、どこにあるか(位置)と何か(値)が同時に取れる。つまり、判定と抽出が同じ操作に統合される。

SubMatches は、グループが捕まえた中身の集合である。正規表現が「構造化された抽出」をできるのは、この仕組みがあるからだ。

4.4 グルーピングとキャプチャ:丸かっこの二つの役割

丸かっこは二つの役割を持つ。ここを分けて理解すると、パターンが読めるようになる。

一つ目は、かたまり化(グルーピング)である。複数トークンを一つの単位として扱い、繰り返しや選択の対象にする。

例として、ab を三回繰り返すなら (ab){3} のように書く。ここでの目的は「構造を作る」ことであり、取り出しではない。

二つ目は、結果取得(キャプチャ)である。丸かっこで囲った部分が、SubMatches として取り出せるようになる。

例として、日付 YYYY-MM-DD から年だけ取りたいなら、年の部分を丸かっこで囲う。目的は「抽出」である。

重要なのは、同じ丸かっこが「構造」と「抽出」を同時に担うことだ。便利だが、抽出が不要なグルーピングまでキャプチャにしてしまうと、SubMatches の番号がずれて読みにくくなる。以降の節は、この問題を避けながら SubMatches を読む方法を固める。

4.5 SubMatches を読む:ネスト・選択・繰り返しで何が取れるか

SubMatches を読めるようになると、正規表現は「判定の道具」から「データ抽出の道具」へ変わる。ここでは、よくある三つの難所を順に押さえる。

ネストは、丸かっこの中に丸かっこが入る形である。基本として、番号は「左から見た開き丸かっこ順」に付く。ネストすると、見た目と番号がズレて感じるので、抽出したい箇所だけを最小限のキャプチャにするのが安全である。

選択は、(A|B) のように複数候補を持つ。マッチした側だけが値を持ち、マッチしなかった側は空になることがある。この「空になり得る」を前提に処理を書く必要がある。抽出後の後処理で Null/Empty を弾く設計が安定する。

繰り返しは、キャプチャと相性が悪い。繰り返しの中のキャプチャは、最終回の値だけが残ることが多い。この挙動を知らないと、「全部取れるはず」と勘違いする。複数回取りたいなら、複数マッチを列挙する設計に切り替える。つまり、1回の Match で全部を取ろうとしない。

この三つを押さえると、SubMatches は「読むべき構造」になる。抽出の設計は、パターンの設計で決まる。後で無理に取り返さない。

4.6 置換の基本:Replace と参照($1 など)で再構成する

置換は、抽出した結果を材料にして、新しい文字列を作る操作である。正規表現の Replace は、パターンにマッチした部分を置換文字列で置き換える。

ここで重要なのが参照である。$1、$2 のように、キャプチャしたグループの内容を置換側で再利用できる。これにより、「抽出→再構成」が一つの式で書ける。

たとえば、姓と名を入れ替える、区切り文字を統一する、余計な空白を削る、などは典型的な置換の用途である。置換は、単に文字を消すだけでなく、抽出した要素を並び替えるための機構でもある。

ただし、置換は便利なぶん、パターンが読めないと危険になる。運用の基本は、まず regTest でマッチ範囲を確認し、regExtract で SubMatches を確認し、その上で regReplace を適用する、という順序である。いきなり置換から入らない。

4.7 ゼロ幅マッチ:文字ではなく「位置」にマッチする

正規表現が他の道具と決定的に違うのは、文字ではなく位置を条件にできる点である。ゼロ幅マッチは、文字を消費しない。つまり、マッチしても長さが0になり得る。

この性質を理解すると、アンカーや境界、先読みがすべて同じ枠で理解できるようになる。ここが腹落ちポイントである。

4.7.1 アンカー・境界・先読みを同じ概念で捉える

アンカーは、先頭や末尾といった位置を指す。典型は ^ と $ である。これらは「文字」ではなく「場所」にマッチする。

境界も位置である。単語境界のように、「文字種が変わる地点」を条件にする。これも文字を消費しない。

先読みは、さらに一段進んだ位置条件である。「この地点の右側がこの形なら OK」という条件を付ける。先読み自体は消費しないため、マッチ結果に含まれないが、受理条件として働く。

つまり、これらはすべて「位置に条件を課す」仕組みであり、ゼロ幅マッチとして統一的に理解できる。

4.7.2 “if 文としての先読み”で条件付き受理を書く

先読みは、実務では「if 文として使う」と理解すると使いどころが明確になる。

たとえば、「この形式っぽいが、特定の語が後ろに続く場合だけ許可する」「この記号の直後が数字なら区切りとみなす」など、条件分岐をパターン内に埋め込める。

重要なのは、先読みは消費しない点である。消費しないから、抽出したい部分のマッチ範囲を汚さずに、条件だけを追加できる。抽出と判定を両立させたい場面で威力が出る。

4.8 設計手順:小さく作って検証し、ログで確認して固める

正規表現の設計は、最初から完成形を狙うと失敗する。最短手順は決まっている。

第一に、最小パターンで受理できるかを確認する。これは regTest でよい。全体一致か部分一致かもここで固定する。

第二に、マッチ範囲と位置を確認する。regExtract で、開始位置と長さと値を見る。ここで「思っていた範囲を食っているか」を確認する。

第三に、必要な要素だけキャプチャする。SubMatches を確認し、番号が意図通りかを見る。抽出が目的なら、キャプチャは最小限にする。

第四に、置換するなら最後に行う。regReplace で結果を作り、置換対象が過不足ないかを確認する。

この順序を守ると、正規表現は怖くなくなる。逆に、いきなり複雑なパターンを書き、いきなり置換まで行うと、失敗しても原因が追えない。正規表現は「小さく作って、見える形で検証する」が最短である。

5 グルーピング、キャプチャ、SubMatches、ゼロ幅マッチ、先読みを最短で腹落ちさせる

5.1 VBScript.RegExp の概要と制限

VBAで一般に使う正規表現は、COMの VBScript.RegExp(Microsoft VBScript Regular Expressions 5.5)を利用する。実行結果は「マッチ全体」を表す Match と、その内部の SubMatches(キャプチャされた部分列の集合)として返る。SubMatches は Execute の結果として作られ、キャプチャ用の丸かっこで囲んだ部分式があるときだけ要素が入る。

このエンジンには、重要な制限がある。

第一に、先読み(lookahead)は使えるが、後読み(lookbehind)は肯定・否定ともにサポートされない。

第二に、名前付きキャプチャは使えず、キャプチャは番号で扱う前提になる。

第三に、Perlや.NETほどの高度な機能(アトミックグループ、所有量指定子、インラインオプション等)を前提にした説明は、そのままでは通用しないことがある。

本章は、この制限を前提に「それでもなお正規表現の急所になる概念」を、VBAで実務に直結する形に絞って整理する。

5.2 本章の主眼(この5つが理解の芯になる)

本章で押さえるのは次の5点である。

(1) グルーピング

複数要素を「ひとまとまり」にして、繰り返しや分岐の単位にする。

(2) キャプチャ

グルーピングした部分列を「記録」して、後で(SubMatchとして)取り出せるようにする。

(3) SubMatches

記録されたキャプチャの値を、番号順に受け取るための入れ物である。Match.Value(全体一致)とは役割が違う。

(4) ゼロ幅マッチ

文字を消費せず、位置条件だけを追加する(^, $, \b など)。先読みもこの仲間である。

(5) 先読み(肯定・否定)

右側の条件を「消費せずに」付け足す。if文に近い感覚で使える。後読みが無いVBAでは特に重要になる。

5.3 サンプルの見方(本章の共通フォーマット)

以降のサンプルは、次の情報をセットで記す。

テキスト:対象文字列

パターン:正規表現

意味:何を受理し、どこを条件にし、どこを記録するか

期待(概念):全体一致(Value)と、キャプチャ(SubMatches)の中身

なお、SubMatches は「キャプチャ丸かっこ」に対応する部分列の集合である。全体一致は Match(Value)側で取り、SubMatchesには入らない。

5.4 グルーピングとキャプチャ(同じ()に2つの役割がある)

グルーピングは単位化、キャプチャは記録である。VBAでは同じ ( ) が両方を兼ねるため、混乱の多くがここで起きる。

サンプル1(最小のキャプチャ)

テキスト:abc

パターン:(a)bc

意味:a を記録しつつ abc を受理する

regExtract "abc", "(a)bc"

期待:Value=abc、SubMatches(0)=a

サンプル2(複数キャプチャ)

テキスト:abc

パターン:(a)(b)c

意味:a と b を別々に記録する

期待:Value=abc、SubMatches(0)=a、SubMatches(1)=b

regExtract "abc", "(a)(b)c"

サンプル3(入れ子の番号感覚)

テキスト:abc

パターン:((a)b)c

意味:外側の()が「ab」を、内側の()が「a」を記録する

期待:Value=abc、SubMatches(0)=ab、SubMatches(1)=a

要点:キャプチャ番号は「左から登場順」で増える、をここで固定する。

regExtract "abc", "((a)b)c"

サンプル4(グルーピングの価値:量指定子の単位)

テキスト:ababab

パターン:(ab){3}

意味:ab を1単位として3回繰り返す(単位化が主目的)

期待:Value=ababab、SubMatches(0)=ab

要点:この例では抽出は副次で、単位化が本体である。

regExtract "ababab", "(ab){3}"

サンプル5(グルーピング無しだと意味が変わる)

テキスト:ababab

パターン:ab{3}

意味:a のあとに b が3回、つまり abbb を狙う(別物になる)

期待:Valueは成立しない場合が多い(テキスト次第)

要点:「どこに量指定子が掛かるか」はグルーピングで制御する。

regExtract "ababab", "ab{3}"

5.5 SubMatches(抽出の急所。Valueとの分業を固定する)

SubMatchesは「キャプチャ丸かっこ」の結果だけを入れる。Executeで作られ、読み取り専用のコレクションである。

サンプル6(全体一致と部分抽出)

テキスト:ID=12345

パターン:ID=(\d+)

意味:全体は受理しつつ、数字だけを記録して取り出す

期待:Value=ID=12345、SubMatches(0)=12345

regExtract "12345", "(\d+)"

サンプル7(区切りの混入と、抽出設計)

テキスト:2025-12-18

パターン:(\d{4})-(\d{2})-(\d{2})

意味:日付を3分割して記録する

期待:Value=2025-12-18、SubMatches(0)=2025、(1)=12、(2)=18

regExtract "2025-12-18", "(\d{4})-(\d{2})-(\d{2})"

参考 regExtract "2025-12-18", "((\d{4})-(\d{2})-(\d{2}))"

参考 regreplace("2025-12-18", "(\d{4})-(\d{2})-(\d{2})","$2$3-$1")

サンプル8(任意部分のキャプチャは空になり得る)

テキスト:color / colour(2ケースで試す)

パターン:colo(u)?r

意味:u があれば記録、無ければ空として記録され得る

期待:colour → SubMatches(0)=u、color → SubMatches(0)=空(または未取得相当)

要点:「無い=失敗」ではなく、「無いが受理は成立」を作れる点が重要である。

regExtract "color ", "colo(u)?r"

regExtract "colour ", "colo(u)?r"

サンプル9(反復中のキャプチャは最後寄りになりやすい)

テキスト:a1 b2 c3

パターン:(\w\d\s*)+	※\w \d \sそれぞれ単独でマッチさせてみる。ちょっと違和感あり。

意味:1回の一致の中で同じキャプチャが更新される

期待(典型):SubMatches(0) が最後の “c3 ” になりやすい

要点:「全部欲しい」要件は、1回の一致に詰め込まず、複数マッチを列挙する設計に切り替えるのが筋である。

regExtract "a1 b2 c3", "(\w\d\s*)+"
参考	regExtract "a1 b2 c3", "(\w\d\s*)"
参考	regExtract "a1 b2 c3", "(\w\d\s)"
参考	regExtract "a1 b2 c3", "\w\d\s"

サンプル10(列挙向きの書き方に変える発想)

テキスト:a1 b2 c3

パターン:(\w)(\d)

意味:1ペアを1マッチとして列挙する(Globalで回す前提)

期待(各マッチのSubMatches):(a,1)、(b,2)、(c,3)

regExtract "a1 b2 c3", "(\w)(\d)"

5.6 ゼロ幅マッチ(文字を消費しない。位置条件だけを足す)

ゼロ幅マッチは「ここが境界か」「ここが先頭か」といった位置の条件だけを判定し、文字を消費しない。結果として、マッチ範囲を増やさずに条件だけを強化できる。

サンプル11(受理の基本:^ と $)

テキスト:abc

パターン:^abc$

意味:文字列全体が abc のときだけ受理する

期待:Value=abc(部分一致ではなく受理の形に固定できる)

regExtract "abc", "^abc$"

サンプル12(受理と検索の差を明確化)

テキスト:xxabcxx

パターン:^abc$

意味:全体がabcでないので受理しない

期待:一致しない

要点:^ と $ は、仕様チェック(受理)に向く。

regExtract "xxabcxx", "^abc$"

参考 regExtract "xxabcxx", "(?=.)"

参考 regExtract "xxabcxx", "(?!.)"

サンプル13(単語境界 \b)

テキスト:cat scatter

パターン:\bcat\b

意味:単語としての cat だけを受理する(scatter内のcatは弾く)

期待:Value=cat

regExtract "cat scatter", "\bcat\b"

サンプル14(ゼロ幅+抽出:条件は境界、抽出は中身)

テキスト:abc 123 def

パターン:\b(\d+)\b

意味:境界条件で数値塊だけを選び、その中身だけを記録する

期待:Value=123、SubMatches(0)=123

regExtract "abc 123 def", "\b(\d+)\b"

5.7 先読み(ゼロ幅のif文。右側条件を消費せずに足す)

先読みは、右側が条件を満たすかを確認するが、その確認自体は文字を消費しない。VBScript系では lookahead は使えるが lookbehind は使えないため、先読みは設計上の主武器になる。

サンプル15(肯定先読み:直後が数字なら成立)

テキスト:abc123

パターン:abc(?=\d)

意味:abc の直後が数字なら abc を受理する(数字は消費しない)

期待:Value=abc

regExtract "abc123", "abc(?=\d)"

サンプル16(否定先読み:直後が数字なら不成立)

テキスト:foo1 fooX

パターン:foo(?!\d)

意味:foo の直後が数字なら弾く

期待:Value=foo(fooX側だけが一致する)

regExtract "foo1 fooX", "foo(?!\d)"

サンプル17(先読みの中でキャプチャして“覗いた値”を取る)

テキスト:abc123

パターン:abc(?=(\d+))

意味:abc を受理しつつ、右側の数字を消費せずに記録する

期待:Value=abc、SubMatches(0)=123 ※実際にはSubMathesの結果は空白でAIの予想と違う

要点:先読みはゼロ幅でも、内部の()は記録として使えることがある。

regExtract "abc123", "abc(?=(\d+))"

参考 regExtract "abc123", "abc(?=\d+)(\d+)"

サンプル18(複数先読みでAND条件を作る)

テキスト:aB3xxxx(例)

パターン:^(?=.[A-Z])(?=.\d).{6,}$

意味:大文字を含む、数字を含む、長さが6以上、をすべて満たすときだけ受理する

期待:条件を満たせば一致、満たさねば不一致 ※実際には受理しない

要点:先読みは「受理条件の合成」に強いが、読みやすさが落ちやすいので本章では“原理の確認”に留める。

regExtract "aB3xxxx", "^(?=.[A-Z])(?=.\d).{6,}$"

5.8 非キャプチャグループ(記録せずに単位化だけする)

非キャプチャグループ (?:...) は、グルーピング(単位化)はしたいが、記録(キャプチャ)は不要な場合に使う表記である。一般的なPerl系記法として広く説明されている。

VBAの実装環境差でエラーになる場合は、( ) に戻して「SubMatchesとして参照しない」運用で目的は達成できる。

サンプル19(不要なキャプチャ番号の増殖を止める)

テキスト:2025-12-18

パターン:(\d{4})-(?:\d{2})-(\d{2})

意味:年と日だけ記録し、月は一致条件として使うだけにする

期待:Value=2025-12-18、SubMatches(0)=2025、SubMatches(1)=18

regExtract "2025-12-18", "(\d{4})-(?:\d{2})-(\d{2})"

サンプル20(ORの単位化は必要だが抽出は不要)

テキスト:gray / grey

パターン:gr(?:a|e)y

意味:aかeかで分岐するが、その違い自体は抽出しない

期待:Value=gray または grey、SubMatchesは無し

regExtract "gray / grey", "gr(?:a|e)y"

参考 regExtract "gray / grey", "gr(?:(a|e))y"

5.9 後読みが無いVBAでの置き換え原則(重要)

VBScript系では lookbehind がサポートされないため、後読み前提のパターンはそのままでは動かない。したがって次のどちらかで置き換える。

原則A:左側条件を“消費して捨てる”形にして、欲しい部分だけをキャプチャする

テキスト:ID:123

(後読みを使いたい形):(?<=ID:)\d+ これはVBAでは不可。

(置き換え):ID:(\d+)

意味:ID: を含めて一致させ、数字だけをSubMatchesで取る

期待:Value=ID:123、SubMatches(0)=123

regExtract "ID:123", "ID:(\d+)"

原則B:仕様を言い換えて、右側条件を先読みで表現する

テキスト:123; 123x

パターン:\d+(?=;)

意味:セミコロンの直前にある数字だけを受理する

期待:Value=123

regExtract "123; 123x", "\d+(?=;)"

5.10 本章のまとめ(この章で“できるようになること”)

(1) ( ) を見たら、「単位化(グルーピング)」と「記録(キャプチャ)」の両面を切り分けて考えるようになる。

(2) 抽出は SubMatches、全体一致は Value、という分業を固定する。

(3) ゼロ幅マッチは「消費しない条件」であり、受理仕様を壊さずに条件を追加できる。

(4) 先読みはゼロ幅の右側条件であり、後読みが無いVBAでは設計の中心になる。

(5) 非キャプチャは「抽出設計を安定させる道具」であり、SubMatchesの番号汚染を防ぐ。