Word VBAを掌握する極限の知見:置換処理の「実行履歴」を監査ログとしてFSOで外部出力する
開発プロジェクトの現場で、Word文書の大量置換マクロを組んだことはないだろうか。
「検索して、置換して、おしまい」——そんな素朴なコードを書いて満足しているうちは、まだ初級者だ。
実務の世界では、「どの文字列が、ドキュメント内のどこで、何回置換されたのか」という実行結果のトレースが求められる。特に仕様書、契約書、公文書の自動生成においては、意図せぬ誤置換や、置換漏れが致命的なコンプライアンス違反や手戻りを引き起こすからだ。
今回は、Word VBAの`Find`オブジェクトの挙動を完全に制御しつつ、FileSystemObject(FSO)を用いて置換の実行履歴をCSV形式で外部出力する、プロダクション品質の堅牢な設計と実装コードを伝授する。
—
なぜ「なんとなく置換するだけのマクロ」は現場で破綻するのか?
多くのプログラマが陥る罠が、`Selection.Find`の多用や、`Range.Find.Execute Replace:=wdReplaceAll`の丸投げだ。これには以下の致命的な欠点がある。
1. 置換回数がブラックボックスになる:`wdReplaceAll`は一括処理で高速だが、「実際に何回ヒットして置換されたのか」をWordは教えてくれない。
2. 誤置換の検知が不可能:例えば「A」を「B」に置換したつもりが、予期せぬ文字列の一部を巻き込んでいても、目視で全ページを確認する以外に知る術がない。
3. 監査証跡(エビデンス)が残らない:金融・医療・製造業の現場では、「いつ、どのルールで文書が改変されたか」のログを残すことが法的・業務的に義務付けられているケースが多い。
これを解決するためには、「ループを回して1件ずつ置換箇所を特定し、置換が発生するたびにカウンタをインクリメントして、最後にCSVへ書き出す」というアプローチが必要になる。
—
堅牢なログ出力アーキテクチャの設計
今回の実装における設計上のキモは以下の3点だ。
1. Dictionaryオブジェクトによる集計管理
どの検索ワードが何回置換されたかをメモリ上で効率的に集計するため、`Scripting.Dictionary`を使用する。
2. FSOによる安全なストリーム書き込み
ログファイル(CSV)の出力には `Scripting.FileSystemObject` を使用。ファイルが他のプロセスでロックされている場合のエラーハンドリングや、UTF-8(BOM付き)での文字化け防止も考慮する。
3. Findオブジェクトの安全なリセット
Wordの`Find`は前回の検索条件(書式やワイルドカード設定など)を保持してしまう特性がある。処理の前後で必ずクリアするサニタイズ処理を入れる。
—
プロダクションコード:監査ログ出力付き置換エンジン
以下のコードをVBAの標準モジュールに貼り付けてほしい。そのまま実務の現場で即戦力として機能する、エラーハンドリング完備の堅牢なコードだ。
Option Explicit
‘================================================================================所有・著作権
‘ 業務自動化アーキテクチャチーム
‘ Module: 監査ログ付き置換エンジン (Word VBA)
‘================================================================================
Sub ExecuteControlledReplaceWithLog()
Dim fso As Object
Dim logFile As Object
Dim targetDoc As Document
Dim dictLog As Object
‘ 置換ルールの定義 (検索ワード, 置換ワード)
Dim searchWords As Variant
Dim replaceWords As Variant
searchWords = Array(“旧システム名”, “株式会社A”, “Ver.1.0”)
replaceWords = Array(“新システム名”, “株式会社B”, “Ver.2.0”)
Dim i As Long
Dim replaceCount As Long
Dim logFolderPath As String
Dim logFilePath As String
‘ 1. 初期化とファイルパスの決定
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set dictLog = CreateObject(“Scripting.Dictionary”)
Set targetDoc = ActiveDocument
‘ ログ保存先はアクティブドキュメントと同じフォルダとする
logFolderPath = targetDoc.Path
If logFolderPath = “” Then
‘ ドキュメントが未保存の場合はデスクトップをフォールバックとする
logFolderPath = CreateObject(“WScript.Shell”).SpecialFolders(“Desktop”)
End If
logFilePath = logFolderPath & “\Replace_Audit_Log_” & Format(Now, “yyyymmdd_HHMMSS”) & “.csv”
‘ 2. 画面描画を停止してパフォーマンスを極限まで引き上げる
Application.ScreenUpdating = False
On Error GoTo ErrorHandler
‘ 3. 置換処理の実行と集計ループ
For i = LBound(searchWords) To UBound(searchWords)
replaceCount = 0
With targetDoc.Content.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = searchWords(i)
.Replacement.Text = replaceWords(i)
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
‘ 1件ずつヒットを確認しながら置換し、カウンタを回す
Do While .Execute(Replace:=wdReplaceNone)
‘ ヒットした部分を置換
.Replacement.Execute
replaceCount = replaceCount + 1
‘ 選択範囲が無限ループにならないようにドキュメント末尾方向へ進める
.Parent.Collapse wdCollapseEnd
Loop
End With
‘ 辞書オブジェクトに結果を格納
dictLog.Add searchWords(i) & “,” & replaceWords(i), replaceCount
Next i
‘ 4. FSOを用いたCSVログの外部出力 (Shift-JIS または UTF-8)
‘ 第3引数(True)でファイル作成を許可、第2引数(2)で書き込みモード(Overwrite)
Set logFile = fso.CreateTextFile(logFilePath, True, False)
‘ CSVヘッダーの書き込み
logFile.WriteLine “Timestamp,SearchWord,ReplaceWord,ReplaceCount,DocumentName”
‘ 辞書からデータを展開して書き込み
Dim key As Variant
Dim timestamp As String
timestamp = Format(Now, “yyyy-mm-dd HH:MM:SS”)
For Each key In dictLog.Keys
logFile.WriteLine timestamp & “,” & key & “,” & dictLog(key) & “,” & targetDoc.Name
Next key
logFile.Close
Application.ScreenUpdating = True
MsgBox “置換処理および監査ログの出力が完了しました。” & vbCrLf & “保存先: ” & logFilePath, vbInformation, “完了”
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
If Not logFile Is Nothing Then logFile.Close
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & “Error: ” & Err.Description, vbCritical, “エラー”
End Sub
—
コードの深掘り:プロが仕込んだ3つの技術的ポイント
1. `wdReplaceNone` による手動ループ制御
一括置換(`wdReplaceAll`)を使わず、`Do While .Execute(Replace:=wdReplaceNone)` というイディオムを採用している点に注目してほしい。
これにより、1回のヒットごとに `replaceCount` を正確にカウントできるだけでなく、将来的に「特定の条件を満たす場合のみ置換をスキップする」といった複雑なビジネスロジックを挟み込む余白(拡張性)が生まれる。
2. 無限ループを防ぐ `.Parent.Collapse wdCollapseEnd`
Word VBAの検索で最も恐ろしいのは、置換後の文字列を再度検索対象として拾ってしまい、無限ループに陥るバグだ。
`.Replacement.Execute` で置換を実行した直後に `.Parent.Collapse wdCollapseEnd` を実行することで、検索開始位置を「いま置換した文字列の末尾」に強制移動させ、二重検知を完全に防いでいる。
3. FSOによる堅牢なI/Oと環境非依存のパス設計
`ActiveDocument.Path` を参照し、文書が未保存であればデスクトップへフォールバックする動的なパス解決を実装している。これにより、ユーザーがどこでマクロを実行しても「ファイルが見つからない(Error 76)」というクラッシュを防ぐことができる。
—
まとめ:自動化のプロフェッショナルを目指すあなたへ
ただ動くだけのコードは、素人にでも書ける。
しかし、「失敗を検知でき、証拠を残し、保守が容易であること」を担保するのが、プロフェッショナルな業務自動化エンジニアの仕事だ。
今回紹介した「実行履歴のログ出力」のパターンは、WordだけでなくExcelやPowerPointのバッチ処理、さらには外部API連携ツール等への応用も利く汎用的なアーキテクチャである。ぜひ自身の開発プロジェクトに組み込み、ワンランク上の自動化システムを構築してほしい。
