【実務・中級編】【上級者向け】検索結果を「正規表現」で検証し、条件を満たす場合のみ置換するハイブリッド検索 – Word VBA解析バイブル

スポンサーリンク

【Word VBA】検索結果を「正規表現」で検証し、条件を満たす場合のみ置換するハイブリッド検索

業務自動化の現場において、Wordの `Find` / `Replacement` オブジェクトの挙動に頭を悩ませた経験はないだろうか。
Wordの標準検索はワイルドカードが使えるとはいえ、その構文は極めて独自仕様かつ貧弱であり、実務で要求される複雑なパターンマッチング(例えば「特定の文脈の中に現れる特定の桁数の数値のみ」など)をこなすのは不可能に近い。かといって、文書全体をVBScriptの正規表現エンジンに丸投げしようにも、Word特有の書式(フォント、スタイル、段落構造など)が破壊されるリスクを孕むため、プロフェッショナルな開発案件では採用し難い。

そこで本稿では、「Wordの高速なネイティブ検索で大まかな候補を絞り込み、ヒットした範囲(Range)をVBScriptのRegExpで厳密にバリデーションした上で置換する」という、両者の強みを極限まで引き出したハイブリッド検索・置換アーキテクチャを伝授する。

—

なぜ「丸投げ」は実務で破綻するのか?

多くの初級〜中級プログラマブルなアプローチとして、文書全体のテキストをごっそり `ActiveDocument.Content.Text` で取得し、RegExpで置換してから文書に戻すという手法を取りがちだ。

しかし、これは以下の理由から本番環境ではご法度である。

1. 書式情報の消失: テキストだけを抽出・置換して書き戻すと、そこに設定されていた太字、マーカー、ハイパーリンク、フィールドコードなどのリッチテキスト情報がすべて吹き飛ぶ。
2. ストーリーの無視: Word文書は「本文(Main Story)」だけでなく、「ヘッダー」「フッター」「テキストボックス」「脚注」など複数のストーリーに分かれている。Contentプロパティだけではこれらを網羅できないか、予期せぬ範囲を書き換えてしまう。
3. パフォーマンスの劣化: 巨大な仕様書や契約書(数百ページ規模)において、全テキストの文字列操作はメモリ効率が悪い。

結論として、検索と絞り込みはWordの `Range` オブジェクトに担当させ、検証のジャッジメントだけを `RegExp` に委ねるべきなのだ。

—

アーキテクチャの全体像

今回実装するハイブリッドエンジンの設計思想は以下の通りだ。

1. スコープの限定: 検索対象を `ActiveDocument.Content`(または選択範囲)に限定し、Wordの最適化された `Find` メソッドで一次候補を高速にヒットさせる。
2. Range単位のキャプチャ: ヒットした `Range.Text` に対し、VBScriptの正規表現(`VBScript.RegExp`)で条件判定を行う。
3. トランザクショナルな置換: 条件に合致した場合のみ、その `Range` のテキストを置換する。この際、書式(FontやStyle)はそのまま保持される。
4. 無限ループの回避: 置換によって検索文字列が再度ヒットし続ける現象(Word VBAのFindにおける悪名高いバグ)を、`Range.Collapse` と `Find.Execute` のループ制御で完全に封じ込める。

—

プロダクションコード実装

以下のコードは、実務の現場でそのまま組み込める堅牢なクラス/モジュール構造を持つ。
今回は、「特定のキーワード(例: `ID-`)の直後にある、ただし特定の文脈を満たす数値のみを対象にして置換する」という、正規表現の真骨頂を発揮するシナリオを想定している。

Option Explicit

‘ ==============================================================================
‘ 処理名: ハイブリッド検索・置換エンジン
‘ 概要 : WordのFind機能で候補を抽出し、RegExpで検証した上で安全に置換する
‘ ==============================================================================
Public Sub ExecuteHybridSearchAndReplace()
Dim targetRange As Range
Dim regEx As Object
Dim matchCollection As Object
Dim searchKeyword As String

‘ — 設定値 —
searchKeyword = “ID-” ‘ WordのFindで高速に一次抽出するためのキーワード

‘ 1. VBScript正規表現エンジンの初期化
‘ ※事前バインディングする場合は “Microsoft VBScript Regular Expressions 5.5” を参照設定すること
Set regEx = CreateObject(“VBScript.RegExp”)
With regEx
‘ パターン例: 「ID-」の後に続く「3桁の数字のみ」を厳密に検証する場合
.Pattern = “^ID-\d{3}$”
.IgnoreCase = False
.Global = False
End With

‘ 2. 検索対象範囲の設定(文書全体)
Set targetRange = ActiveDocument.Content

‘ 3. Wordネイティブ検索の構成
With targetRange.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = searchKeyword
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False

‘ 4. ループ処理による一次候補の走査
Do While .Execute
‘ WordのFindは「一致した範囲」を targetRange に自動拡張する
‘ ここで、より広いスニペット(例:前後の文脈を含めた文字列)を検証したい場合は
‘ targetRange.Duplicate などを拡張して調整する。

‘ 5. RegExpによるバリデーション
If regEx.Test(targetRange.Text) Then
‘ — 条件を満たした場合の置換処理 —
‘ 例として、マッチした文字列の末尾を「-APPROVED」に書き換える
‘ 書式(太字など)は保持される
targetRange.Text = targetRange.Text & “-APPROVED”

‘ 置換を行った場合、Rangeは置換後の末尾に移動するため、
‘ 無限ループを防ぐためにポインタを適切に進める
targetRange.Collapse wdCollapseEnd
Else
‘ 条件不一致の場合は、次の検索へ進むためにRangeの末尾へ移動
targetRange.Collapse wdCollapseEnd
End If

‘ 文書の終端に達した際の安全装置
If targetRange.End >= ActiveDocument.Content.End Then Exit Do
Loop
End With

‘ 6. クリーンアップ
Set regEx = Nothing
Set targetRange = Nothing

MsgBox “ハイブリッド置換処理が正常に完了しました。”, vbInformation, “処理完了”
End Sub

—

開発現場で押さえるべき「エンジニアリングの急所」

このコードを運用するにあたり、シニアエンジニアとして知見を共有しておくべきポイントがある。

1. `wdFindStop` の重要性

`Find.Wrap` プロパティに `wdFindContinue` を指定すると、文書の末尾に達したときに先頭に戻ってループが無限ループ(デッドロック)に陥る危険性がある。ハイブリッド検索では、自前で `targetRange.End >= ActiveDocument.Content.End` を監視してループを抜ける設計にするため、`Wrap` は必ず `wdFindStop` に設定すること。

2. `Range.Text` 書き換えの副作用

`targetRange.Text = “…”` を実行した瞬間、その `Range` に含まれていたカスタムフォントや段落属性以外のインライン書式がリセットされる仕様がWordには存在する。
もし「文字の赤字などの書式を完全に維持したままテキストの一部だけを書き換えたい」という高度な要件がある場合は、`targetRange.Text` の一括置換ではなく、`targetRange.Characters` や `XML (WordprocessingML)` レベルでの操作、あるいは `Find.Replacement.Text` への置き換えを検討する必要がある。ただし、大半の実務要件においては、今回の `Range` 単位の置換で十分なパフォーマンスと実用性を発揮する。

3. 参照設定(Early Binding)の推奨

今回のコードは、移植性を考慮して `CreateObject(“VBScript.RegExp”)`(レイトバインディング)を採用しているが、実際の開発プロジェクトやチーム運用の際は、VBEの「ツール」>「参照設定」から 「Microsoft VBScript Regular Expressions 5.5」 にチェックを入れ、アーリーバインディング(`Dim regEx As New RegExp`)に変更することを強く推奨する。
これにより、IntelliSense(入力補完)が効くだけでなく、実行速度の最適化とコードの保守性が飛躍的に向上する。

—

総括

Word VBAにおけるテキスト処理は、一歩間違うと文書の破損やパフォーマンス低下を招く地雷原である。しかし、Wordの得意分野(高速なインデックス検索と範囲特定)と、VBScriptの得意分野(厳密なパターンマッチング)を適切に役割分担させることで、どんなに複雑なビジネスロジックも堅牢に自動化することが可能となる。

本稿で提示したハイブリッドアプローチをあなたの武器に加え、実務の自動化精度を次のステージへと引き上げてほしい。

タイトルとURLをコピーしました