📊連載第20回！初心者のためのExcel VBA入門：正規表現でファイル名からRev番号を抽出しよう🔍 バージョン管理が簡単に！

# Excel VBAにおける正規表現を活用したファイル名解析テクニック

私はVBAの活用経験を通じて得た知識を整理し、共有する目的で記事を作成しているプログラミング歴1年半になるエンジニアです。前回は、VBAでよく使用する処理をFunction化してコードの重複を減らし保守性を高める実践的なユーティリティ関数の作成テクニックについて解説しました。今回は、正規表現を活用してファイル名から特定のパターンを抽出するテクニックについて詳しく説明します。

- [第1回: Excel VBAの基礎知識とセキュリティ設定](https://qiita.com/CodeLeaf/items/2d3e3185a725480fecc3)
- [第2回: Excel VBAの基本操作とオブジェクトの理解](https://qiita.com/CodeLeaf/items/c65d2531908aaf53649d)
- [第3回: Excel VBAにおける変数と定数の基本](https://qiita.com/CodeLeaf/items/3b36eddf7b5a8b5c5d99)
- [第4回: Excel VBAにおけるシート操作の基本とエラー処理](https://qiita.com/CodeLeaf/items/573fb7c61a756aa123ea)
- [第5回: Excel VBAにおける条件分岐](https://qiita.com/CodeLeaf/items/8be52d682baa097a65c1)
- [第6回: Excel VBAにおける繰り返し処理の基本](https://qiita.com/CodeLeaf/items/5bc6c961a090e550bca9)
- [第7回: Excel VBAにおける配列とFor Eachの活用](https://qiita.com/CodeLeaf/items/bdd8d488e49086219663)
- [第8回: Excel VBAにおけるFormulaとValueの使い分けとユーザー入力の取得](https://qiita.com/CodeLeaf/items/ecf312a53f7ede91bce9)
- [第9回: Excel VBAにおけるファイル操作とフォルダ管理の基本](https://qiita.com/CodeLeaf/items/e5520321e760364f6aae)
- [第10回: Excel VBAにおけるFileSystemObjectを活用した高度なファイル操作](https://qiita.com/CodeLeaf/items/a78e9c7dd2b7e68f1d9e)
- [第11回: Excel VBAにおけるFileSystemObjectを活用した高度なファイル操作 応用編](https://qiita.com/CodeLeaf/items/4607db0151dddfb7f458)
- [第12回: Excel VBAにおけるStrConv関数の活用と応用テクニック](https://qiita.com/CodeLeaf/items/4f6358548bced6c047e0)
- [第13回: Excel VBAにおけるワークブックの安全な操作と管理テクニック](https://qiita.com/CodeLeaf/items/cf136898dd41c7dd5d9f)
- [第14回: Excel VBAにおけるFunction（関数）の作成と活用テクニック](https://qiita.com/CodeLeaf/items/a299f465bdcb1957e2be)
- [第15回: Excel VBAにおける配列を返す関数の作成と活用テクニック](https://qiita.com/CodeLeaf/items/711a93ac86b5c4150e13)
- [第16回: Excel VBAにおけるコレクションの活用と応用テクニック](https://qiita.com/CodeLeaf/items/6a28c65d5f8d19964664)
- [第17回: Excel VBAにおける辞書型（Dictionary）の活用と応用テクニック](https://qiita.com/CodeLeaf/items/5f85b2029e8d51eaa788)
- [第18回: Excel VBAにおけるEnum型を活用した関数設計と実装テクニック](https://qiita.com/CodeLeaf/items/3e91ef9556799d96aeea)
- [第19回: Excel VBAにおけるユーティリティ関数の作成と活用テクニック](https://qiita.com/CodeLeaf/items/bc156b6a9cadd7b10601)
- 第20回: Excel VBAにおける正規表現を活用したファイル名解析テクニック（本記事）

## 目次

- [はじめに](#はじめに)
- [正規表現とは](#正規表現とは)
- [正規表現メタ文字一覧](#正規表現メタ文字一覧)
- [VBAで正規表現を使用するための準備](#vbaで正規表現を使用するための準備)
- [リビジョンとバージョンの違いについて](#リビジョンとバージョンの違いについて)
- [Rev番号抽出関数の詳細解説](#rev番号抽出関数の詳細解説)
- [正規表現パターンの理解](#正規表現パターンの理解)
- [活用例と応用テクニック](#活用例と応用テクニック)
- [エラー処理とベストプラクティス](#エラー処理とベストプラクティス)
- [まとめ](#まとめ)

## はじめに

ファイル管理業務において、ファイル名から特定の情報を抽出する処理は頻繁に発生します。特に、設計書や仕様書などのドキュメント管理では、「Rev1」「Rev.02」「rev03」といったリビジョン番号をファイル名から自動的に抽出し、バージョン管理や一覧作成に活用することが多々あります。正規表現を使用することで、これらの問題を効率的に解決し、より柔軟で保守性の高いコードを記述することができます。

今回は、実際の業務でよく遭遇するRev番号抽出処理を例に、VBAでの正規表現活用テクニックを詳しく解説します。

## 正規表現とは

正規表現とは、文字列のパターンを表現するための特殊な記法です。「文字列の中から特定のパターンを検索したい」「文字列が特定の形式に合致するかチェックしたい」といった処理を、簡潔に実現できます。

### 正規表現の特徴とメリット

**パターンマッチングの柔軟性**: 従来の文字列検索では、完全一致やワイルドカードを使用した部分検索に限られていました。これに対し、正規表現では「数字が1桁以上続く」「英字で始まり数字で終わる」といった複雑な条件パターンを指定できます。この高度な柔軟性により、多様なバリエーションを持つファイル名に対しても、単一のパターンで効率的に対応することが可能になります。

**コードの簡潔性**: 複雑な文字列解析処理を短いパターン記述で表現できるため、コード全体の可読性が向上します。例えば、InStr関数やMid関数を何度も組み合わせて記述していた処理を、一行の正規表現パターンで置き換えることができます。

**国際標準の記法**: 正規表現は多くのプログラミング言語で共通して使用される記法であるため、一度覚えれば他の言語でも活用できます。また、Excelの検索機能やテキストエディタの置換機能でも使用されることが多く、汎用性が高い技術です。

**高度な文字列操作**: 検索だけでなく、マッチした部分の抽出、置換、分割など、様々な文字列操作を効率的に行うことができます。これにより、ファイル名の正規化や情報抽出などの処理を柔軟に実装できます。

## 正規表現メタ文字一覧

正規表現を効果的に活用するためには、メタ文字（特殊な意味を持つ文字）の理解が不可欠です。

### 文字マッチング系メタ文字

正規表現では、特定の文字や文字の種類をマッチさせるための専用の記号が用意されています。

| メタ文字 | 意味 | 使用例 | マッチする例 |
|----------|------|--------|--------------|
| `.` | 任意の1文字（改行以外） | `a.c` | abc, a1c, a_c |
| `\d` | 数字1文字（0-9） | `\d\d` | 01, 99, 42 |
| `\w` | 英数字とアンダースコア1文字 | `\w\w\w` | abc, A12 |
| `\s` | 空白文字1文字（スペース、タブなど） | `\s\s` | 連続した2つの空白 |

**実際の使用場面での理解**

例えば、ファイル名から日付部分を抽出したい場合、`\d{4}-\d{2}-\d{2}`というパターンを使用することで、「2024-01-15」のような形式の日付を正確に抽出できます。ここで`\d{4}`は「数字4桁」を意味し、年の部分にマッチします。

### 量詞（繰り返しを表すメタ文字）

文字の繰り返し回数を指定するために使用される記号です。これらを理解することで、より柔軟なパターンマッチングが可能になります。

| メタ文字 | 意味 | 使用例 | マッチする例 |
|----------|------|--------|--------------|
| `*` | 直前の文字が0回以上 | `ab*c` | ac（bが0回）、abc（bが1回）、abbc（bが2回）|
| `+` | 直前の文字が1回以上 | `ab+c` | abc, abbc（acはマッチしない） |
| `?` | 直前の文字が0回または1回 | `ab?c` | ac, abc（abbcはマッチしない） |
| `{n}` | 直前の文字がちょうどn回 | `a{3}` | aaa |
| `{n,m}` | 直前の文字がn回以上m回以下 | `a{2,4}` | aa, aaa, aaaa |

**業務での活用例**

Rev番号の抽出でよく使用される`\d+`は、「数字1文字以上」を意味します。これにより、「Rev1」「Rev123」のように桁数が異なる番号でも、一つのパターンで対応できるのです。

### 文字クラス（角括弧を使った文字指定）

特定の文字の範囲や組み合わせを指定する際に使用します。角括弧内に記述した文字のいずれか1文字がマッチします。

| メタ文字 | 意味 | 使用例 | マッチする例 |
|----------|------|--------|--------------|
| `[abc]` | a、b、cのいずれか1文字 | `[Rr]ev` | Rev, rev |
| `[a-z]` | 小文字のアルファベット1文字 | `[a-z]+` | abc, test |
| `[0-9]` | 数字1文字（\dと同じ） | `[0-9]{2}` | 01, 99 |
| `[^abc]` | a、b、c以外の1文字 | `[^0-9]` | 数字以外の文字 |

**実践的な使用方法**

ファイル名で「Ver」または「ver」を検索したい場合、`[Vv]er`というパターンを使用することで、大文字小文字の両方に対応できます。これは、IgnoreCaseプロパティ（後述）を使用しない場合の代替手段としても有効です。

### 位置指定メタ文字

文字列内の特定の位置を指定するために使用される記号です。これらを活用することで、文字列の開始や終了位置を条件に含めたマッチングが可能になります。

| メタ文字 | 意味 | 使用例 | マッチする例 |
|----------|------|--------|--------------|
| `^` | 文字列の開始 | `^Rev` | Revで始まる文字列 |
| `$` | 文字列の終了 | `\.txt$` | .txtで終わる文字列 |
| `\b` | 単語境界 | `\bRev\b` | 単語としてのRev |

**単語境界の理解**

`\b`（単語境界）は特に重要な概念です。例えば、「Revision」という文字列から「Rev」を抽出したくない場合、`\bRev\b`というパターンを使用することで、独立した単語としての「Rev」のみをマッチさせることができます。

### エスケープが必要な特殊文字

正規表現では特別な意味を持つ文字を、文字通りの意味で使用したい場合にエスケープ（`\`を前に付ける）が必要です。

| 文字 | エスケープ後 | 使用例 | 備考 |
|------|-------------|--------|------|
| `.` | `\.` | `Ver\.1` | ドット文字そのものをマッチ |
| `*` | `\*` | `file\*\.txt` | アスタリスク文字そのもの |
| `+` | `\+` | `C\+\+` | プラス文字そのもの |
| `?` | `\?` | `help\?` | クエスチョン文字そのもの |

### 初心者が覚えるべき優先順位

正規表現のメタ文字は数多く存在しますが、実際のファイル名解析では以下の順序で覚えることをお勧めします。

**第1段階（必須）**: `.`（任意の文字）、`\d`（数字）、`+`（1回以上）、`*`（0回以上）

**第2段階（よく使用）**: `{n,m}`（回数指定）、`[abc]`（文字クラス）、`?`（0回または1回）

**第3段階（応用）**: `^`（行頭）、`$`（行末）、`\b`（単語境界）

これらのメタ文字を段階的に覚えることで、徐々に複雑なパターンマッチングが可能になり、より実用的な正規表現を記述できるようになります。

## VBAで正規表現を使用するための準備

VBAで正規表現を使用するためには、事前に参照設定を行う必要があります。この設定により、正規表現専用のオブジェクトを使用できるようになります。

### 参照設定の手順

参照設定は以下の手順で行います。

1. **VBA エディタを開く**: Alt + F11 キーでVBA エディタを起動します
2. **参照設定ダイアログを開く**: メニューバーから「ツール」→「参照設定」を選択します
3. **ライブラリを選択**: 一覧から「Microsoft VBScript Regular Expressions 5.5」を見つけてチェックを入れます

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/d752a3ac-38db-4e23-9ff5-4494a3ca2265.png)


4. **設定を保存**: 「OK」ボタンをクリックして設定を確定します

この設定により、RegExpオブジェクトやMatchCollectionオブジェクトなど、正規表現処理に必要なオブジェクトが使用可能になります。

### 主要なオブジェクトの理解

#### RegExpオブジェクト

正規表現の設定と実行を行う中心的なオブジェクトです。検索パターンの設定（`Pattern`）、大文字小文字の区別設定（`IgnoreCase`）、グローバル検索（`Global`）の設定などを行います。

```vb
' RegExpオブジェクトを作成
Dim regEx As New RegExp

' 検索パターンを設定
regEx.Pattern = "rev.\d+"

' 大文字小文字を区別しない
regEx.IgnoreCase = True

' 全てのマッチを検索する
regEx.Global = True
```

#### MatchCollectionオブジェクト

正規表現の実行結果として得られるマッチした部分の集合（検索で見つかった全てのマッチ結果を格納するコレクション）を表します。Globalプロパティが `True` の場合、複数のマッチが格納されます。それぞれのマッチ情報をインデックス（0から始まる）でアクセスできます。

```vb
' 検索を実行してマッチ結果を取得
Dim matches As MatchCollection
Set matches = regEx.Execute("Rev1_Rev2.txt")

' マッチした数を確認
Debug.Print matches.Count     ' 2が表示される

' インデックス0（1つ目のマッチ）の値を表示
Debug.Print matches(0).Value  ' "Rev1"が表示される

' インデックス1（2つ目のマッチ）の値を表示
Debug.Print matches(1).Value  ' "Rev2"が表示される
```

#### Matchオブジェクト

個別のマッチ結果を表すオブジェクトです。マッチした文字列の内容（`Value`）、位置（`FirstIndex`）、長さ（`Length`）などの詳細情報を取得できます。

```vb
' 最初のマッチ結果を取得
Dim firstMatch As Match
Set firstMatch = matches(0)

' マッチした文字列を取得
Debug.Print firstMatch.Value      ' "Rev1"が表示される

' マッチした位置を取得（0から開始）
Debug.Print firstMatch.FirstIndex ' 0が表示される

' マッチした文字列の長さを取得
Debug.Print firstMatch.Length     ' 4が表示される
```

#### Executeメソッド

RegExpオブジェクトのExecuteメソッドは、指定した文字列に対して正規表現検索を実行します。このメソッドがMatchCollectionオブジェクトを返すため、検索結果を取得できます。

```vb
' Executeメソッドで検索を実行
Dim targetString As String
targetString = "設計書_Rev1.xlsx"

' 検索結果をMatchCollectionオブジェクトとして取得
Set matches = regEx.Execute(targetString)

' 検索が成功したかどうかを確認
If matches.Count > 0 Then
    Debug.Print "検索に成功しました"
Else
    Debug.Print "マッチする文字列が見つかりませんでした"
End If
```

#### Testメソッド

RegExpオブジェクトのTestメソッドは、指定した文字列が正規表現パターンにマッチするかどうかをTrue/Falseで判定します。マッチ結果の詳細は不要で、単純に「マッチするか」だけを知りたい場合に使用します。

```vb
' Testメソッドでパターンマッチを判定
If regEx.Test(targetString) Then
    Debug.Print "Rev番号が含まれています"  ' この結果が表示される
Else
    Debug.Print "Rev番号が含まれていません"
End If
```

#### Replaceメソッド

RegExpオブジェクトのReplaceメソッドは、正規表現パターンにマッチした部分を指定した文字列に置換します。文字列の一括置換や正規化処理に便利です。

```vb
' Replaceメソッドでパターンマッチした部分を置換
Dim replacedText As String

' 検索パターンを設定
regEx.Pattern = "rev"

' Rev番号を「Version」に置換
replacedText = regEx.Replace(targetString, "Version")

Debug.Print replacedText  ' "設計書_Version1.xlsx"が表示される
```

#### SubMatchesオブジェクト

SubMatchesオブジェクトは、正規表現のキャプチャグループにマッチした部分を格納するコレクションオブジェクトです。キャプチャグループとは、正規表現パターンの一部を括弧`()`で囲むことで、その部分にマッチした内容を個別に取得できる機能のことです。

**キャプチャグループの基本概念**

キャプチャグループを使用することで、マッチした文字列全体だけでなく、その一部分も個別に取得できるようになります。例えば、`"(rev\.)(\d+)"`というパターンでは、括弧で囲まれた部分がそれぞれキャプチャグループとなります。

```vb   
' キャプチャグループを含むパターンを設定
' 第1グループ: (rev\.) - 「rev」部分をキャプチャ
' 第2グループ: (\d+) - 数字部分をキャプチャ
regEx.Pattern = "(rev\.)(\d+)"
    
' マッチした文字列全体を表示
Debug.Print "マッチした全体: " & matches(0).Value                 ' "Rev.01"が表示される

' 各キャプチャグループの内容を表示
' SubMatches(0) = 第1キャプチャグループの内容
Debug.Print "第1グループ（rev部分）: " & matches(0).SubMatches(0) ' "Rev."が表示される

' SubMatches(1) = 第2キャプチャグループの内容
Debug.Print "第2グループ（数字部分）: " & matches(0).SubMatches(1) ' "1"が表示される
```

SubMatchesオブジェクトは配列のような構造を持ち、インデックス番号（0から開始）を使用して各キャプチャグループの内容にアクセスできます。キャプチャグループが設定されていない正規表現パターンの場合、SubMatchesコレクションは空になります。

また、キャプチャグループにマッチする内容が存在しない場合、該当するSubMatchesの要素は空文字列または空の値となります。これを活用することで、オプション的な文字列の存在をチェックすることも可能です。

SubMatchesオブジェクトを活用することで、文字列の分解や部分的な置換処理を効率的に行うことができ、より高度な文字列操作が実現できるようになります。

#### 正規表現オブジェクトのプロパティ・メソッド一覧

| オブジェクト | プロパティ・メソッド | 説明 |
|-------------|---------------------|------|
| RegExp | Pattern | 検索パターンを設定 |
| RegExp | IgnoreCase | 大文字小文字を区別するか（True/False） |
| RegExp | Global | 全てのマッチを検索するか（True/False） |
| RegExp | Execute | 正規表現検索を実行（MatchCollectionを返す） |
| RegExp | Test | パターンマッチするかどうかを判定（True/Falseを返す） |
| RegExp | Replace | マッチした部分を指定文字列に置換 |
| MatchCollection | Count | マッチした数 |
| Match | Value | マッチした文字列 |
| Match | FirstIndex | マッチした位置 |
| Match | Length | マッチした文字列の長さ |
| Match               | SubMatches          | キャプチャグループにマッチした部分（コレクション） |

## リビジョンとバージョンの違いについて

正規表現を使ってファイル名から番号を抽出する前に、「リビジョン」と「バージョン」の違いについて説明します。実際の業務では、これらの用語が混在して使われることが多く、ファイル名の命名規則を理解する上で基本的な知識となります。

### リビジョン（Revision）とは

リビジョンとは、同じドキュメントや成果物に対する「改訂」「修正」を表す概念です。主に以下のような特徴があります。

**細かな修正や改善**: 誤字脱字の修正、軽微な内容の追加・削除、レイアウトの調整など、基本的な内容は変更せずに品質を向上させる変更を指します。

**連続的な番号付け**: 通常、Rev1、Rev2、Rev3のように連続した番号で管理されます。小数点を使用してRev1.1、Rev1.2のように細分化する場合もあります。

**同一文書の改良**: あくまで同じ文書の改良版であり、根本的な内容や目的は変わりません。

### バージョン（Version）とは

バージョンとは、より大きな変更や機能追加を含む「版」を表す概念です。主に以下のような特徴があります。

**大幅な変更や機能追加**: 新機能の追加、仕様の大幅な変更、構成の見直しなど、内容に大きな変化がある場合に使用されます。

**段階的な番号付け**: V1.0、V2.0のようにメジャーバージョンと、V1.1、V1.2のようにマイナーバージョンを組み合わせて管理することが一般的です。

**新しい段階への移行**: 単なる修正ではなく、新しい段階や世代への移行を意味します。

### 正規表現での対応における注意点

ファイル名から番号を抽出する際は、組織や部署によって命名規則が異なることを理解しておく必要があります。

**表記のバリエーション**: 「Rev」「Revision」「Ver」「Version」「V」など、略語や表記方法が統一されていない場合があります。

**番号体系の違い**: 「Rev01」のようにゼロ埋めされている場合や、「V1.2.3」のように複数の区切り文字が使用される場合があります。

**組み合わせパターン**: バージョンとリビジョンが同時に使用される場合、どちらを優先的に抽出するかを決めておく必要があります。

このような背景を理解した上で、次のセクションで解説する正規表現パターンを設計することで、より実用的で柔軟なファイル名解析が可能になります。

## Rev番号抽出関数の詳細解説

この関数は、ファイル名から「Rev1」「Rev.02」「rev03」といった様々な形式のリビジョン番号を抽出することを目的としています。

```vb
' 引数として受け取ったファイル名からRev番号を抽出する関数
Function ExtractRevNumber(fileName As String) As String
    
    ' 正規表現を扱うためのオブジェクトを新規作成
    Dim regEx As New RegExp
    
    ' 正規表現の検索結果を格納するコレクション変数を宣言
    Dim matches As MatchCollection
    
    ' 正規表現オブジェクトの設定
    With regEx
        
        ' 検索パターンを設定（rev+任意の1文字+1桁以上の数字）
        .Pattern = "rev.\d+"
        
        ' 大文字小文字を区別しない設定
        .IgnoreCase = True
        
    ' 正規表現オブジェクトの設定を終了
    End With
    
    ' ファイル名に対して正規表現検索を実行し、結果をmatchesに格納
    Set matches = regEx.Execute(fileName)
    
    ' マッチした結果が1つ以上存在するかチェック
    If matches.Count > 0 Then
        
        ' 最初にマッチした結果を関数の戻り値として設定
        ExtractRevNumber = matches(0).Value
        
    ' マッチしなかった場合は空文字列が戻り値になる
    End If
    
    ' matchesオブジェクトのメモリを解放
    Set matches = Nothing
    
    ' regExオブジェクトのメモリを解放
    Set regEx = Nothing
    
End Function
```

### 関数の設計思想と重要なポイント

この関数では、実用性と保守性を重視した設計を採用しています。

**安全な戻り値設計**: マッチした場合はRev番号を文字列で返し、マッチしなかった場合は空文字列を返します。これにより、呼び出し側で結果を簡単に判定できます。NothingやErrorを返すのではなく、空文字列という明確で扱いやすい値を選択しています。

**柔軟性の確保**: 「Rev」「rev」「REV」といった大文字小文字の違いや、「Rev1」「Rev.01」「Rev_02」といった区切り文字の違いに対応できる設計となっています。実際の業務にて、ファイル名の命名規則が統一されていないこともあるため、この柔軟性は重要です。

**単一責任の原則**: この関数は「Rev番号の抽出」という単一の明確な責任のみを持ちます。ファイルパスの処理や拡張子の除去などの他の処理は含めず、抽出機能に特化することで、再利用性と保守性を向上させています。

**メモリ管理**: 使用したオブジェクトは最後にNothingを設定してメモリを解放しています。これはVBAでオブジェクトを使用する際の基本的なルールです。

**エラー処理**: この基本版では簡潔性を重視してエラー処理を省略していますが、実際の業務では適切なエラー処理を追加することをお勧めします。


### 変数宣言とオブジェクト初期化の詳細

```vb
Dim regEx As New RegExp
Dim matches As MatchCollection
```

これらの変数宣言では、正規表現処理に必要な専用オブジェクトを定義しています。RegExpオブジェクトは正規表現エンジンの本体であり、MatchCollectionオブジェクトは検索結果を格納するコレクションです。


### 正規表現設定の詳細分析

```vb
With regEx
    .Pattern = "rev.\d+"
    .IgnoreCase = True
End With
```

**Patternプロパティ**: 検索したい文字列のパターンを定義します。今回の例では`"rev.\d+"`というパターンを設定しており、これは「rev」の後に任意の1文字が続き、その後に1桁以上の数字が続くパターンを表現しています。

**IgnoreCaseプロパティ**: Trueに設定することで、大文字小文字を区別しない検索を行います。これにより、「Rev」「rev」「REV」のいずれの形式でもマッチするようになります。実際の業務では命名規則が統一されていない場合も意外と多いため、この設定は非常に重要です。

### 検索実行と結果処理のロジック

```vb
Set matches = regEx.Execute(fileName)
If matches.Count > 0 Then ExtractRevNumber = matches(0).Value
```

Executeメソッドにより、指定されたファイル名文字列に対して正規表現検索を実行します。結果はMatchCollectionオブジェクトとして返され、マッチした部分の情報が格納されます。

マッチした結果が1つ以上存在する場合（matches.Count > 0）、最初のマッチ結果（matches(0)）のValueプロパティを取得して戻り値として設定します。インデックスが0から始まることに注意が必要です。

### メモリ管理の重要性

```vb
Set matches = Nothing
Set regEx = Nothing
```

VBAでは、オブジェクト変数を使用した後に明示的にNothingを設定することで、メモリの解放を促進できます。特に、正規表現オブジェクトは内部的に複雑な処理を行うため、適切なメモリ管理が重要です。この処理により、メモリリークの防止とパフォーマンスの向上が期待できます。

## 正規表現パターンの理解

提供されたコード例で使用されている正規表現パターン`"rev.\d+"`について、各要素の意味と動作を詳しく解説します。

### パターン構成要素の詳細分析

**rev部分**: これは文字通り「rev」という文字列にマッチします。IgnoreCaseがTrueに設定されているため、「rev」「Rev」「REV」のいずれの形式でもマッチします。

**ドット（.）部分**: ドットは正規表現では「任意の1文字」を表す特殊文字（メタ文字）です。改行文字以外のあらゆる文字（英字、数字、記号、スペースなど）にマッチします。これにより、「Rev1」「Rev.01」「Rev_02」「Rev-03」などの様々な区切り文字に対応できます。

**\d+部分**: `\d`は「数字1文字」を表し、`+`は「直前の文字が1回以上の繰り返し」を表します。したがって`\d+`は「数字が1桁以上連続する」という意味になります。これにより、「1」「01」「123」などの様々な桁数の数字にマッチします。

### パターンマッチングの具体例

このパターンがどのような文字列にマッチするかを具体例で確認しましょう。

- **「設計書_Rev1.xlsx」**: 「Rev1」部分がマッチします
- **「仕様書Rev.02_最終版.docx」**: 「Rev.02」部分がマッチします  
- **「manual_rev_123.pdf」**: 「rev_123」部分がマッチします
- **「REV-99-updated.txt」**: 「REV-99」部分がマッチします

一方、以下のような文字列にはマッチしません。

- **「Version1.xlsx」**: 「rev」が含まれていないためマッチしません
- **「Rev.xlsx」**: 数字が含まれていないためマッチしません
- **「Rev_ABC.docx」**: 数字ではなく文字が続いているためマッチしません

### パターンの改良案と応用

より厳密な制御が必要な場合は、以下のようなパターンの変更を検討できます：

**より限定的なパターン**: `"rev[._-]?\d+"`では、区切り文字をドット、アンダースコア、ハイフンのいずれか、または無しに限定できます。`[._-]?`の`?`は「0回または1回の出現」を表します。

**数字桁数の制限**: `"rev.\d{1,3}"`では、数字の桁数を1桁から3桁までに制限できます。`{1,3}`は「1回以上3回以下の繰り返し」を表します。

**大文字小文字の明示的制御**: `"[Rr]ev.\d+"`では、IgnoreCaseプロパティを使用せずに、パターン内で大文字小文字の両方を明示的に指定できます。

## 活用例と応用テクニック

Rev番号抽出関数の基本的な使用方法と、実際の業務で役立つ応用例を紹介します。

### 基本的な使用例

```vb
Sub TestRevExtraction()
    Dim fileName As String
    Dim revNumber As String
    
    ' テスト用のファイル名
    fileName = "設計書_Rev.05_最終版.xlsx"
    
    ' Rev番号を抽出
    revNumber = ExtractRevNumber(fileName)
    
    ' 結果を確認
    If revNumber <> "" Then
        Debug.Print "抽出されたRev番号: " & revNumber
    Else
        Debug.Print "Rev番号が見つかりませんでした"
    End If
End Sub
```

### 最新版ファイルの特定

実際の業務では、複数のバージョンが存在するファイルから、最新のRev番号を持つファイルを特定する処理をすることが多いでしょう。

```vb
' ファイル配列から最新のRev番号を持つファイルを特定する関数
Function FindLatestRevFile(fileList As Variant) As String

    ' 数字抽出用の正規表現オブジェクトを作成
    Dim regEx New RegExp
    
    ' 正規表現の設定
    With regEx
    
        ' 数字のみを抽出するパターンを設定
        .Pattern = "\d+"
        
        ' 大文字小文字を区別しない設定
        .IgnoreCase = True
        
        ' 最初にマッチした数字のみを取得する設定
        .Global = False
        
    End With
    
    ' 最大のRev番号を記録する変数を宣言・初期化
    Dim maxRevNum As Long
    maxRevNum = 0
    
        ' 最新版として特定されたファイル名を格納する変数
    Dim latestFile As String
    
    ' 配列の全要素を順次処理するループ
    Dim i As Long
    For i = LBound(fileList) To UBound(fileList)
    
        ' 現在処理中のファイルから抽出したRev番号を格納
        Dim currentRev As String
        currentRev = ExtractRevNumber(CStr(fileList(i)))
        
        ' Rev番号が正常に抽出できなかった場合は次のファイルへ
        If currentRev = "" Then GoTo NextFile
        
        ' 正規表現の実行結果を格納
        Dim matches As MatchCollection
        Set matches = regEx.Execute(currentRev)
        
        ' 数字が見つからなかった場合は次のファイルへ
        If matches.Count = 0 Then GoTo NextFile
        
        ' Rev番号の数字部分を数値として格納
        Dim currentRevNum As Long
        currentRevNum = CLng(matches(0).Value)
        
        ' 現在の数値が最大値以下の場合は次のファイルへ
        If currentRevNum <= maxRevNum Then GoTo NextFile
        
        ' 最大Rev番号を更新
        maxRevNum = currentRevNum
        
        ' 最新ファイル名を更新
        latestFile = CStr(fileList(i))

NextFile:
    ' 次のファイルへ処理を続行
    Next i
    
    ' 関数の戻り値として最新ファイル名を設定
    FindLatestRevFile = latestFile
End Function
```

### Rev番号を1増やして新規保存する実用的な関数

実際の業務では、現在のファイルのRev番号を1つ増やして新しいファイルとして保存したい場面がよくあります。

#### Rev番号インクリメント（増加）関数の作成

```vb
' 現在のファイル名のRev番号を1増やした新しいファイル名を生成する関数（短縮版）
Function IncrementRevFileName(currentFileName As String) As String
    
    ' ファイル名が空の場合は早期リターン
    If Len(Trim(currentFileName)) = 0 Then
        IncrementRevFileName = currentFileName
        Exit Function
    End If
        
    ' 正規表現オブジェクトを作成
    Dim regEx As New RegExp

    ' 正規表現の設定
    With regEx
        ' Rev番号パターン（キャプチャグループを使用）
        .Pattern = "(rev\.)(\d+)"
        .IgnoreCase = True
        .Global = False
    End With
    
    ' Rev番号が見つからない場合は元のファイル名を返す
    If Not regEx.Test(currentFileName) Then
        IncrementRevFileName = currentFileName
        Goto Cleanup
    End If
    
    ' Matchesオブジェクトを一度だけ取得して効率化
    Dim matches As MatchCollection
    Set matches = regEx.Execute(currentFileName)
    
    ' 数字部分を取得し、1を加算してゼロ埋め
    Dim currentNum As String: currentNum = matches(0).SubMatches(1)
    Dim newNum As String
    newNum = Format(CLng(currentNum) + 1, String(Len(currentNum), "0"))
    
    ' $1 = "rev." 部分、$2 = 数字部分を新しい数字に置換
    IncrementRevFileName = regEx.Replace(currentFileName, "$1" & newNum)

Cleanup:
    ' リソースの解放
    Set regEx = Nothing
    
End Function
```

### 重要な処理部分の解説

#### SubMatchesオブジェクトによるグループ内容の取得

```vb
' .Pattern = "(rev\.)(\d+)"

' SubMatchesオブジェクトを使用してキャプチャグループの内容を取得
' matches(0).SubMatches(0) = 第1キャプチャグループ（"rev."部分）
' matches(0).SubMatches(1) = 第2キャプチャグループ（数字部分）
Dim currentNum As String: currentNum = matches(0).SubMatches(1)
```

**Format関数とString関数による桁数保持**

**Format関数**とは、数値や日付を指定した書式で文字列に変換するVBAの関数です。ここでは数値のゼロ埋め（先頭を0で埋める）に使用しています。

```vb
' 例：数値を3桁のゼロ埋めで表示
Format(5, "000")    ' 結果: "005"
Format(42, "000")   ' 結果: "042"
Format(123, "000")  ' 結果: "123"
```

**String関数**とは、指定した文字を指定した回数繰り返した文字列を作成するVBAの関数です。

```vb
' 例：指定文字を繰り返す
String(3, "0")      ' 結果: "000"
String(5, "*")      ' 結果: "*****"
```

この2つの関数を組み合わせることで、元の数字の桁数に合わせたゼロ埋め文字列を動的に作成できます。

```vb
' 数字部分を取得し、1を加算してゼロ埋め
Dim currentNum As String: currentNum = matches(0).SubMatches(1)
Dim newNum As String: newNum = Format(CLng(currentNum) + 1, String(Len(currentNum), "0"))
```

**置換での参照記号の使用**

正規表現の`Replace`メソッドでは、キャプチャグループの内容を`$1`、`$2`のような参照記号で呼び出せます。これにより、元の文字列の一部を保持しながら、特定の部分のみを置換できます。

```vb
' 参照記号を使用した置換処理の詳細
' $1 = "rev." 部分、$2 = 数字部分を新しい数字に置換
IncrementRevFileName = regEx.Replace(currentFileName, "$1" & newNum)

' 例:   「設計書_Rev.01.xlsx」の場合
' $1 = "Rev."
' newNum = "02"  
' 結果: 「設計書_Rev.02.xlsx」
```

この方法により、「Rev.」を自動的に保持しながら、数字部分のみを正確に更新できるようになります。

## エラー処理とベストプラクティス

正規表現を使用する際の適切なエラー処理と、実用性を高めるためのベストプラクティスについて解説します。

### 堅牢なエラー処理の実装例

```vb
' ファイル名から「revXX」形式のリビジョン番号を抽出する関数
' fileName: リビジョン番号を抽出する対象のファイル名
' errorInfo: エラー発生時にエラー情報を格納する参照渡し変数
Function ExtractRevNumberSafe(fileName As String, ByRef errorInfo As String) As String
        
    On Error GoTo ErrorHandler
    
    ' 引数の検証
    If Len(Trim(fileName)) = 0 Then
        errorInfo = "ExtractRevNumberSafe: ファイル名が空です"
        ExtractRevNumberSafe = ""
        Exit Function
    End If
    
    ' 正規表現オブジェクト変数
    Dim regEx As RegExp
    Dim matches As MatchCollection
    
    ' 正規表現オブジェクトを作成
    Set regEx = New RegExp
    
    ' 正規表現の設定
    With regEx
        .Pattern = "rev.\d+"
        .IgnoreCase = True
    End With
    
    ' 正規表現を実行
    Set matches = regEx.Execute(fileName)
    
    ' 結果の取得
    If matches.Count > 0 Then
        ExtractRevNumberSafe = matches(0).Value
    Else
        ExtractRevNumberSafe = ""
    End If
    
Cleanup:    
    ' リソースの解放
    Set matches = Nothing
    Set regEx = Nothing
    
    Exit Function
    
ErrorHandler:
    ' エラー情報の設定
    errorInfo = "ExtractRevNumberSafe: " & Err.Description & " (エラー番号: " & Err.Number & ")"
    ExtractRevNumberSafe = ""
    
    Resume Cleanup ' Resumeで再開
    
End Function
```

:::note info

#### GoTo文とResume文の違いについて
- **GoTo文**
    単純に指定したラベルに処理を移動させる命令です。エラー状態を保持したまま処理が移動するため、エラー情報（Err.NumberやErr.Description）は残り続けます。

- **Resume文**
    エラー処理専用の制御文で、エラー状態を完全にクリアしてから処理を移動します。エラー情報もリセットされるため、移動先では正常な状態として処理が実行されます。

先ほどの`ExtractRevNumberSafe`関数では、エラー情報を記録した後に正常な状態でリソース解放を行いたいため、`Resume Cleanup`を使用しています。
:::

### パフォーマンス最適化のポイント

**正規表現オブジェクトの再利用**: 同じパターンで複数の文字列を処理する場合は、正規表現オブジェクトを使い回すことでパフォーマンスが向上します。オブジェクトの作成にはコストがかかるため、繰り返し処理では特に効果的です。

**適切なパターン設計**: 過度に複雑な正規表現パターンは処理速度を低下させる可能性があります。必要な機能のみを含む、シンプルで効率的なパターンを設計することが重要です。

**早期リターンの活用**: 引数の妥当性チェックなど、正規表現処理を行う前に判定できる条件は、関数の最初で処理することで無駄な処理を削減できます。

### 実用性向上のための設計改良

より実用的な関数にするための改良案として、以下のような拡張が考えられます：

**複数マッチへの対応**: 一つのファイル名に複数のRev番号が含まれている場合への対応や、すべてのマッチを配列で返すオプションの追加。

**数値としての抽出**: Rev番号の数字部分のみを数値として取得するオプションの追加。これにより、バージョン比較処理が容易になります。

**カスタムパターンの対応**: 関数の引数として正規表現パターンを指定できるようにすることで、様々な命名規則に対応可能になります。

## まとめ

VBAにおける正規表現の活用は、文字列処理の可能性を大幅に広げる実践的なテクニックです。今回紹介したRev番号抽出関数は、日常的な業務で頻繁に遭遇するファイル管理処理を効率化する実用的な例として、正規表現の基本的な使い方から実装時の注意点まで、幅広く解説しました。

正規表現の導入により、従来のInStr関数やMid関数では複雑になりがちな文字列解析処理を、簡潔かつ柔軟なコードで実現できるようになります。特に、ファイル名の多様性に対応する必要がある環境では、その価値は非常に高いものです。

ただし、正規表現は強力である反面、適切なエラー処理とパフォーマンスへの配慮が必要な技術でもあります。今回解説したベストプラクティスを参考に、安全で効率的な正規表現処理を実装し、日々の業務効率化に役立ててください。

次回は、より具体的な業務シーンに焦点を当て、「選択したセル範囲に重なる図形だけをまとめて削除したい」というニーズに応えるVBAマクロについて解説します。大量の図形が配置されたシートの整理など、特定の課題解決に役立つコードを紹介する予定です。VBAによるオブジェクト操作のスキルをさらに深め、日々の作業効率を一層向上させるヒントをお届けしますので、ぜひご期待ください。