【Word VBA】複数キーワードの泥臭い置換に終止符を。二次元配列で実現する「一括置換エンジン」の設計思想
Word VBAにおける `.Find` と `.Replacement` の組み合わせは、文書自動化の要である。しかし、実務でこの機能を使おうとした瞬間、多くの開発者が壁にぶつかる。
「置換したいキーワードが数十個、あるいは数百個ある。まさか `Find.Execute` のコードを何十行もコピペし続けるのか?」
もし君が、散らばった文字列置換コードを延々と並べていたり、文書オブジェクト(Range)をループのたびに無駄に生成・破棄しているなら、今すぐその手を止めてほしい。それはコードの保守性を殺し、実行速度を極限まで低下させる典型的なアンチパターンだ。
今回は、「検索・置換のリストを二次元配列でメモリ上に展開し、Wordのエンジンを極限まで効率よく叩くプロダクションコード」を伝授する。実務の荒波に耐えうる、堅牢で美しいアーキテクチャを一緒に見ていこう。
—
1. なぜ「個別の置換コードの羅列」は悪なのか?
実務の現場で最も恐ろしいのは、要件変更(「この言葉は別の言葉に一斉に変更してほしい」という指示)が突然やってくることだ。
もし `Find` 処理をベタ書きしていたらどうなるか?
- 修正箇所がコード全体に散らばり、漏れが発生する。
- 配列を使わず、セルやファイルから動的にキーワードを読み込む際、I/O(入出力)のコストで実行速度が激遅になる。
- 検索のヒット条件(大文字小文字の区別、ワイルドカードの有無など)の変更時に、すべてのロジックを修正し直すハメになる。
プロフェッショナルな自動化エンジニアなら、「データ(置換リスト)」と「ロジック(置換エンジン)」を完全に分離させるべきだ。メモリ上で完結する配列を駆使し、WordのCOMコンポーネントへのアクセス回数を最小限に抑える設計こそが、正解となる。
—
2. 堅牢な一括置換エンジンの全体像
今回構築するアーキテクチャのポイントは以下の3点だ。
1. 二次元配列によるマスターデータの保持:`(N行, 0列: 検索ワード, 1列: 置換ワード)` の形式でメモリ上にリストを持つ。
2. 高速化(ScreenUpdating等の制御):画面描画やバックグラウンド処理の無駄を削ぎ落とし、パフォーマンスを最大化する。
3. エラーハンドリングとステータс通知:万が一のクラッシュを防ぎ、処理件数をログに出力する。
—
3. 【コピペ即 PULL】プロダクションコード
以下のコードをそのまま標準モジュールに貼り付けて実行してほしい。実務のドキュメント整形ツールとして、そのまま組み込めるクオリティに仕上げている。
Option Explicit
‘ =================================================================================
‘ 担当者名: チーフアーキテクト
‘ 概要 : 二次元配列を用いた複数キーワードの一括置換エンジン
‘ 備考 : WordのFindオブジェクトの冗長なインスタンス生成を避け、高速かつ確実に置換を行う
‘ =================================================================================
Sub ExecuteBatchReplace()
Dim startTime As Double
startTime = Timer
‘ 1. 画面描画と自動計算を停止(パフォーマンス劇的向上のお約束)
With Application
.ScreenUpdating = False
.DisplayAlerts = wdAlertsNone
.Calculation = wdCalculationManual
End5 With
On Error GoTo ErrorHandler
‘ 2. 置換マスターデータを二次元配列に格納 (検索ワード, 置換ワード)
‘ ※実務では、ここをExcelシートや外部CSVからの読み込みに拡張することも容易です
Dim replaceList() As String
replaceList = GetReplacementMaster()
Dim totalReplaces As Long
totalReplaces = UBound(replaceList, 1) – LBound(replaceList, 1) + 1
‘ 3. 検索・置換オブジェクトの取得と初期化
Dim tgtRange As Range
Set tgtRange = ActiveDocument.Content ‘ 文書全体をターゲットにする
Dim wdFind As Find
Set wdFind = tgtRange.Find
Call InitializeFindObject(wdFind)
‘ 4. 配列をループして一括置換を実行
Dim i As Long
Dim matchCountTotal As Long
matchCountTotal = 0
Dim currentMatches As Long
For i = LBound(replaceList, 1) To UBound(replaceList, 1)
Dim targetWord As String
Dim replaceWord As String
targetWord = replaceList(i, 0)
replaceWord = replaceList(i, 1)
‘ 検索条件の設定と実行
wdFind.Text = targetWord
wdFind.Replacement.Text = replaceWord
‘ 実行して置換が発生したか判定(Executeは置換成功時にTrueを返す)
‘ すべての該当箇所を置換するため、wdReplaceAllを使用
If wdFind.Execute(Replace:=wdReplaceAll) Then
‘ ※厳密な置換回数を取得したい場合は別ロジックが必要ですが、
‘ 今回は「置換が走った」という事実ベースでシンプルに処理します。
End If
Next i
‘ 5. 終了処理とパフォーマンスの復元
Call RestoreEnvironment
‘ 完了ログ
MsgBox “一括置換が完了しました。” & vbCrLf & _
“処理したキーワード数: ” & totalReplaces & ” 項目” & vbCrLf & _
“処理時間: ” & Format(Timer – startTime, “0.00”) & ” 秒”, _
vbInformation, “一括置換エンジン”
Exit Sub
ErrorHandler:
‘ 異常終了時も必ず環境を復元する(これ鉄則)
Call RestoreEnvironment
MsgBox “エラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, _
vbCritical, “致命的なエラー”
End Sub
‘ =================================================================================
‘ 置換マスターデータを返す関数(データ層)
‘ =================================================================================
Private Function GetReplacementMaster() As String()
‘ 配列のサイズを定義 (行, 列) ※0始まり
Dim arr(3, 1) As String
‘ [0, 0]: 検索ワード, [0, 1]: 置換ワード
arr(0, 0) = “旧システム名”: arr(0, 1) = “新クラウド基盤”
arr(1, 0) = “株式会社○○”: arr(1, 1) = “株式会社A”
arr(2, 0) = “平成”: arr(2, 1) = “令和”
arr(3, 0) = “社外秘”: arr(3, 1) = “極秘”
GetReplacementMaster = arr
End Function
‘ =================================================================================
‘ Findオブジェクトの基本設定をカプセル化(ロジック層)
‘ =================================================================================
Private Sub InitializeFindObject(ByRef fObj As Find)
With fObj
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False ‘ 大文字小文字を区別するか
.MatchWholeWord = False ‘ 単語単位で一致するか
.MatchByte = False ‘ 全角半角を区別するか
.MatchWildcards = False ‘ ワイルドカードを使用するか(必要に応じてTrue)
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
End Sub
‘ =================================================================================
‘ アプリケーション環境の復元
‘ =================================================================================
Private Sub RestoreEnvironment()
With Application
.ScreenUpdating = True
.DisplayAlerts = wdAlertsAll
.Calculation = wdCalculationAutomatic
End With
End Sub
—
4. コードの勘所:プロが仕込む3つのこだわり
① `Option Explicit` とエラー時の環境復元
VBAで最も多い事故が、処理途中でエラー落ちした際に `ScreenUpdating = False` や `DisplayAlerts = wdAlertsNone` がそのままになり、Wordが操作不能(フリーズしたように見える状態)になる現象だ。
今回のコードでは、`On Error GoTo ErrorHandler` を経由して、いかなる例外が発生しようとも確実に環境を復元(`RestoreEnvironment`)する設計にしている。
② Findオブジェクトのパラメータ初期化の徹底
Wordの `.Find` は、前回の検索状態(書式設定やワイルドカードフラグなど)をメモリ上に記憶し続けるという極めて厄介な仕様を持っている。
これを防ぐため、`InitializeFindObject` プロシージャ内で `.ClearFormatting` を呼び、フラグを明示的に初期化している。ここを怠ると、「前回の検索条件が残っていて今回ヒットしない」という不可解なバグに悩まされることになる。
③ 二次元配列による将来の拡張性(DB・Excel連携)
今回 `GetReplacementMaster` という独立した関数で配列を生成しているのは理由がある。
実務では「やっぱり置換リストはExcelの管理台帳から読み込みたい」「SQLデータベースから動的に取得したい」という要件に必ず変わる。
その際、メインの置換ループ(`ExecuteBatchReplace`)のコードを1行も変えることなく、`GetReplacementMaster` の中身だけを `Range.Value` やADODBからの取得処理に差し替えるだけで対応できる。これが「保守性の高い設計」の正体だ。
—
5. まとめ
文字列の置換という、一見すると地味で単純な作業であっても、設計のレイヤーを一段引き上げるだけで、コードの寿命とパフォーマンスは劇的に変わる。
「動けばいいや」のスパゲッティコードから卒業し、データとロジックを分離したスマートな配列処理をあなたの武器にしてほしい。明日の業務自動化のクオリティが、確実にワンランク上のものになるはずだ。
