【VBA】vba-regexを使用した正規表現(VBScript廃止の代替)

# はじめに
2027年頃には「VBScript」はデフォルトで無効になり、その少し後には Windows から完全に削除されます。
VBScriptの非推奨範囲は`vbscript.dll`のみだと[Microsoft windows-itpro-blog](https://techcommunity.microsoft.com/blog/windows-itpro-blog/vbscript-deprecation-timelines-and-next-steps/4148301)のコメントにありました。

>The scope of VBScript deprecation includes only vbscript.dll and no other libraries. This shall not impact any projects that are not dependent on vbscript.dll.

最大の影響は`vbscript.dll`内の正規表現(Regular Expressions)です。
つまり、Microsoft VBScript Regular Expressions 5.5を参照して`New RegExp`としたり、CreateObject("VBScript.RegExp")が使用できなくなります。
これは、`Excel`と`Acccess`の VBA で正規表現を使用する上では参照しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/c09c83a3-45d2-47b2-9876-35bba080da4c.png)

そこで代替案として見つけたのが、VBAのみで記述されたオープンソースの`vba-regex` になります。

# vba-regexの紹介
ExcelでWebスクレイピングする「[SeleniumVBA](https://github.com/GCuser99/SeleniumVBA)」を久しぶりに見たところ、Creditsに「vba-regex by sihlfall」を見つけました。2024/7/26にInitial commitされています。

https://github.com/sihlfall/vba-regex

VbaRegex は、完全に VBA/VB 6 で記述された正規表現エンジンです。JavaScript正規表現をサポートすることを目的としています。

【2025/06/09追記】
>UnicodeReIsWordchar が文字 (az、AZ) のみを「単語文字」として扱い、数字 (0-9) とアンダースコア (_) を誤って除外している
https://github.com/GCuser99/SeleniumVBA/discussions/158

という指摘があったため、vba-regexが修正されています。

## 導入方法
1.上記の[GitHubページ](https://github.com/sihlfall/vba-regex)から、`<> Code`ボタンをクリックして「DownLoad ZIP」を選択します。
2.ダウンロードされた「vba-regex-master.zip」ファイルを展開します。

モジュールファイルが複数に分かれているのですが、1つのファイルにまとめる方法も用意されています。よって、2つのパターンを記載します。

### 複数ファイルをインポート
`src`フォルダの中にある下記ファイルをインポートします。

* ArrayBuffer.bas
* RegexAst.bas
* RegexBytecode.bas
* RegexCompiler.bas
* RegexDfsMatcher.bas
* RegexErrors.bas
* RegexIdentifierSupport.bas
* RegexLexer.bas
* RegexNumericConstants.bas
* RegexRanges.bas
* RegexReplace.bas
* RegexUnicodeSupport.bas
* StaticRegex.bas
* StaticStringBuilder.bas

### 単一ファイルをインポート
複数ファイルは管理など面倒だという方のために、`aio`フォルダの中にあるPowerShellスクリプトの`make_aio.ps1`を実行すると、`build`フォルダの中に複数ファイルを1つのモジュールに集約した`StaticRegexSingle.bas`が作成されます。

#### Singleの除去
モジュールの名前を`Single`を取り`StaticRegex.bas`とします。
メモ帳などで`StaticRegex.bas`を開いて`VB_Name`からも`Single`を取ります。

```vb
Attribute VB_Name="StaticRegexSingle"
 ↓
Attribute VB_Name="StaticRegex"
```

この`StaticRegex.bas`をインポートします。

:::note info
`Single`を取らない場合、宣言する際に`StaticRegexSingle`で定義します。
それだけなので、不要であればSingleの除去なしで使用してください。
例 Dim re As StaticRegexSingle.RegexTy 
:::


## 使用方法
StaticRegexで定義されたままのパターンとVBScript.RegExpのラッパークラスを作成したものを使用するサンプルを提示します。

|対象文字列|正規表現|
|:--|:--|
|Abc123DEFGH4567ijkl890|(\D+)(\d+)|

3つのグループ分けの各サブグループを出力する。

```:出力結果
Abc
123
DEFGH
4567
ijkl
890
```

### デフォルト
StaticRegexで定義された方法になります。

```vb
Dim re As StaticRegex.RegexTy
Dim matcherState As MatcherStateTy
Dim sourceString As String
Dim i As Long
Dim ignoreCase As Boolean, globalFlag As Boolean, multiLine As Boolean
Dim submatchString As String
Dim matchStart As Long, matchLength As Long

globalFlag = True
multiLine = False
ignoreCase = False
StaticRegex.InitializeRegex re, "(\D+)(\d+)", ignoreCase
StaticRegex.InitializeMatcherState matcherState, Not globalFlag, multiLine
sourceString = "Abc123DEFGH4567ijkl890"

Do While StaticRegex.MatchNext(matcherState, re, sourceString)
    With matcherState.captures
        For i = 0 To .nNumberedCaptures - 1
            matchStart = .numberedCaptures(i).start
            matchLength = .numberedCaptures(i).Length
            submatchString = vbNullString
            If matchStart > 0 Then submatchString = Mid$(sourceString, matchStart, matchLength)
            Debug.Print submatchString
        Next
    End With
Loop
```

### RegExラッパー
VBScript.RegExpのラッパークラスになります。
[SeleniumVBAのRegExp.twin](https://github.com/GCuser99/SeleniumVBA/blob/main/src/twinBASIC/Sources/RegExp.twin) が元です。[twinBASIC]()用のため、1ファイルに複数クラスが定義されているのですが、Excelでは1ファイル1クラスとなります。筆者の方で分割しました。

1点だけ修正したのが、`Global`プロパティの名前です。予約語で使用できなかったため、~~`GlobalFlag`に変更しました。~~

【2025/06/09追記】
SeleniumVBAでも専用に作成されました。
https://github.com/GCuser99/vba-regex/tree/master/vbs_regexp/VBA

これを見ると`Global`プロパティの名前は、`GlobalMatch`となっていたため、合わせることにしました。

```vb
Dim re As New RegExp
Dim mc As MatchCollection
Dim m As Match
Dim i As Long

re.Pattern = "(\D+)(\d+)"
re.GlobalMatch = True
Set mc = re.Execute("Abc123DEFGH4567ijkl890")
For Each m In mc
    For i = 0 To m.SubMatches.Count - 1
        Debug.Print m.SubMatches(i)
    Next
Next
```

## ソースコード
サンプルコードと、`StaticRegex.bas` と RegExpのラッパークラス 4ファイルを筆者の GitHub に配置しました。`dist`フォルダに格納してあります。

https://github.com/yaju/vba-regex-example

【2025/06/09追記】
筆者のものより、2025/06/06にSeleniumVBAから正式に分離した下記ソースコードを使用した方がいいと思いいます。

https://github.com/GCuser99/vba-regex/tree/master/vbs_regexp/VBA

### Attribute NewEnum
もし、For Eachでコンパイルエラーが出るようでしたら、`Attribute NewEnum`の定義をプロパティに埋め込む必要があります。GitHub 上のソースコードでは埋め込んであります。

https://tsukiusagi.biz/vba-class-newenum/

# 最後に
VBScript の代替は、PowerShell になります。

IE 廃止の時もそうでしたが、ギリギリになってからではバタバタしてしまいます。
まだ2年ありますが、システム改修には時間がかかりますので、今の内に対応しておくといいでしょう。

https://qiita.com/yaju/items/d3aaee0bf876d86c3c69