はじめに:なぜあなたの「検索・置換」は現場で牙を剥くのか
Word VBAにおけるテキスト処理は、Excelのセル操作とは本質的に異なる。Excelが「座標」の制御であるのに対し、Wordは「ストリーム(流れ)」の制御だからだ。
特に`Find`オブジェクトを用いたワイルドカード検索(正規表現に似たパターンマッチング)は、極めれば数千ページの文書を一瞬で構造化できる強力な武器になる。しかし、凡百のエンジニアが書くコードは、往々にして無限ループに陥るか、書式情報を破壊するか、あるいは実行速度が致命的に遅い。
本稿では、単なるリファレンスの引き写しではない。大規模プロジェクトを完遂させるチーフアーキテクトの視点から、Wordの`Find`オブジェクトを完全に掌握し、堅牢な業務自動化ツールを構築するための「極限の知見」を伝授する。
—
1. オブジェクトモデルの急所:RangeとFindの主従関係
まず、最も重要な原則を叩き込む。絶対に `Selection` オブジェクトを使うな。
`Selection` はユーザーの画面表示を奪い、パフォーマンスを著しく低下させる。プロが操作するのは常に `Range` オブジェクトである。
Rangeの収縮を理解せよ
`Find.Execute` が成功した瞬間、対象となった `Range` オブジェクトは「見つかった文字列そのもの」の範囲に収縮(再定義)される。これが初心者が無限ループに陥る最大の原因だ。
‘ 悪い例:無限ループの温床
Dim rng As Range
Set rng = ActiveDocument.Content
Do While rng.Find.Execute(FindText:=”[0-9]{3}”, MatchWildcards:=True)
‘ 見つかったrngに対して処理
‘ rngは今、見つかった3桁の数字を指している
‘ 次のループで再び「同じ場所」を検索し続ける
Loop
この挙動を制御するには、処理後に `rng.Collapse wdCollapseEnd` を実行し、検索の開始点を「見つかった箇所の直後」へ移動させる必要がある。この基本ができていないコードはプロダクション環境ではゴミに等しい。
—
2. 実践的ワイルドカード:Word特有の癖を突く
Wordのワイルドカードは標準的な正規表現(PCRE等)とは似て非なるものだ。特に実務で多用する以下の記法は、設計時に即座に引き出せるようにしておくこと。
- `[A-Z]{2,}` : 2文字以上の英大文字の連続
- `\<[A-Za-z]` : 単語の始まり
- `([0-9]{4})/([0-9]{2})/([0-9]{2})` : 日付のグルーピング。置換時に `\1年\2月\3日` で参照可能
- `^13` : 段落記号(ワイルドカード使用時は `^p` ではなく `^13` を使うのが鉄則)
—
3. プロダクション・グレードの実装例:複雑なパターン抽出と書式置換
ここでは、実務で頻出する「特定のパターン(例:商品コード [A-Z]{2}-[0-9]{4})を検出し、それに特定のスタイルを適用しつつ、ログを出力する」という堅牢な関数を示す。
このコードは、エラーハンドリング、画面更新の停止、そしてRangeの適切な管理を網羅している。
”’
”’
Public Sub MasterWildcardProcessor()
Dim targetDoc As Document
Set targetDoc = ActiveDocument
‘ 1. 検索パターン定義(例:AB-1234 のような商品コード)
Const PATTERN_CODE As String = “([A-Z]{2})-([0-9]{4})”
‘ 2. パフォーマンス最適化
Application.ScreenUpdating = False
Dim searchRange As Range
Set searchRange = targetDoc.Content
‘ Findオブジェクトの設定
‘ Rangeを直接操作することで、Selectionを動かさず高速処理
With searchRange.Find
.ClearFormatting ‘ 以前の検索条件をクリア
.Replacement.ClearFormatting
.Text = PATTERN_CODE
.Forward = True
.Wrap = wdFindStop ‘ 範囲の末尾で停止(重要)
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True ‘ ワイルドカードを有効化
End With
‘ 3. 実行ループ
On Error GoTo ErrorHandler
Do While searchRange.Find.Execute
‘ この時点で searchRange は「見つかった文字列」を指している
‘ — 業務ロジック開始 —
‘ 例:見つかったテキストにボールド(太字)と赤色を適用
searchRange.Font.Bold = True
searchRange.Font.ColorIndex = wdRed
‘ 例:外部DBやログ出力のためにマッチしたテキストを取得
Debug.Print “Found: ” & searchRange.Text
‘ — 業務ロジック終了 —
‘ 4. 次の検索のためにRangeを「見つかった箇所の直後」に畳む
‘ これを忘れると、同じ箇所を永遠に検索し続ける
searchRange.Collapse wdCollapseEnd
‘ searchRangeが文書の最後まで達していないか確認(念のための安全策)
If searchRange.End >= targetDoc.Content.End – 1 Then Exit Do
Loop
CleanUp:
Application.ScreenUpdating = True
MsgBox “処理が完了しました。”, vbInformation
Exit Sub
ErrorHandler:
Debug.Print “Error: ” & Err.Description
Resume CleanUp
End Sub
—
4. アーキテクトが教える「保守性」の極意
検索条件の外部化
ハードコードされた検索パターンは負債である。実務では、検索パターンや置換ルールをExcelやJSON、あるいは外部の設定ファイルから読み込む設計にすべきだ。
`.Wrap = wdFindStop` の絶対遵守
`wdFindContinue` を使用すると、文書の最後まで行った後に最初に戻って検索を続けてしまう。ループ処理内でこれを使うのは自殺行為だ。必ず `wdFindStop` を指定し、ループ内で `Range` を制御せよ。
書式検索の罠
`Find.Execute` はテキストだけでなく、フォントサイズや色などの「書式」を条件にできる。しかし、ワイルドカードと書式検索を組み合わせる場合、Wordの内部エンジンが複雑な挙動を示すことがある。
もし「特定のフォントの、特定のパターン」を探すなら、まず `Find.Font` で絞り込み、ヒットした `Range.Text` に対して別途VBA標準の `Like` 演算子や `RegExp` オブジェクトでダブルチェックをかける方が、結果的にバグが少なく保守性が高い。
—
5. まとめ:Wordを「プログラム」として扱え
Word VBAは「ワープロの延長」ではない。数万行の構造化データを処理する「テキストプロセッサ」のAPIである。
1. Rangeオブジェクトを支配せよ。
2. `Collapse` メソッドでライフサイクルを制御せよ。
3. ワイルドカードの癖(`^13` 等)を熟知せよ。
この3点を守るだけで、あなたの書くコードの信頼性は劇的に向上する。ツール作成者から、真の自動化エンジニアへ。この知見を、明日からのプロダクトコードに刻み込んでほしい。
