【実務・中級編】【中級者向け】置換処理中に「特定の単語」をスキップする除外リストの実装 – Word VBA解析バイブル

スポンサーリンク

Word VBAを掌握する極限の知見:除外リスト制御による「守りの置換エンジン」の構築

Wordの置換機能(`Find & Replace`)は非常に強力だ。しかし、実務でドキュメントの整形を自動化する際、必ずといっていいほど「壁」にぶぶかる。

「特定のキーワードは一括置換したいが、特定の文脈にある特定の単語だけは絶対に置換してはならない」

例えば、「Client」という単語をすべて「顧客」に置換したいとする。しかし、ブランド名である「Client X」や、特定コード内の「Client_ID」はそのまま残さなければならない。素朴な全置換をかけてしまえば、文書は破壊される。

今回は、配列を用いた「除外リスト(Exclusion List)」をメモリ上に展開し、ヒットするたびに条件分岐でこれを検知・回避する、実務に耐えうる堅牢な置換エンジンの設計思想と実装コードを伝授する。

—

1. なぜ「力技の置換」は破綻するのか

初学者が陥る最大の罠は、Wordの標準機能である `Selection.Find.Execute Replace:=wdReplaceAll` を安易にループさせることだ。あるいは、愚直に正規表現パターンの否定先読み(Negative Lookahead)をWordの不完全なワイルドカード機能で実現しようとして自滅するケースも後を絶たない。

Word VBAにおける検索・置換の本質的なボトルネックは以下の2点に集約される。

1. ドキュメントのオブジェクトモデルへの過剰なアクセス:セルや段落を1つずつループ処理すると、COMの往復(Marshaling)が発生し、パフォーマンスが劇的に低下する。
2. コンテキスト(文脈)の欠如:`wdReplaceAll` は文脈を理解しない。機械的に文字列の一致だけで置換を実行するため、誤爆を防ぐガードが必要になる。

これを解決するのが、「検索は一括、判定は個別に、処理は高速に」行えるメモリ内制御ロジックである。

—

2. 堅牢な置換エンジンのアーキテクチャ設計

今回構築するエンジンの設計方針は以下の通りだ。

  • 除外キーワードの配列管理:ハードコーディングを避け、可変長配列(`String()`)で除外リストを保持する。将来的なCSVやデータベースからの動的読み込みにも耐える構造にする。
  • `Range.Find` オブジェクトの徹底活用:選択範囲(`Selection`)ではなく、`Range`オブジェクトを操作することで、画面描画(ScreenUpdating)の負荷を排除し、処理速度を極限まで高める。
  • 前方・後方コンテキストの検証:ターゲットがヒットした際、その周辺文字列(あるいは完全一致するブロック)が除外リストに該当するかを自作の関数で判定する。

—

3. プロダクションコード:除外リスト付き置換エンジン

以下のコードは、コピー&ペーストするだけで即座に実務の現場で稼働するモジュールである。エラーハンドリングと、処理速度担保のための最適化を施してある。

Option Explicit

‘ ==============================================================================
‘ 処理名: 堅牢な除外リスト付き置換エンジン
‘ 概要: 指定したキーワードを置換する際、除外リストに該当する語句をスキップする
‘ ==============================================================================
Public Sub ExecuteProtectedReplace()
‘ 画面描画と警告を停止し、パフォーマンスを最大化
With Application
.ScreenUpdating = False
.DisplayAlerts = wdAlertsNone
End With

On Error GoTo ErrorHandler

‘ 1. 設定値の定義
Const TARGET_KEYWORD As String = “Client”
Const REPLACE_STRING As String = “顧客”

‘ 除外したいキーワードの配列(必要に応じて外部ファイルやDBから動的生成可能)
Dim exclusionList() As String
exclusionList = Split(“Client_ID,Client X,LegacyClient”, “,”)

‘ 2. 検索範囲の取得(アクティブドキュメントのメインストーリー)
Dim rngTarget As Range
Set rngTarget = ActiveDocument.Content

‘ 3. 検索・置換の実行オブジェクト設定
Dim fnd As Find
Set fnd = rngTarget.Find

With fnd
.ClearFormatting
.Replacement.ClearFormatting
.Text = TARGET_KEYWORD
.Replacement.Text = REPLACE_STRING
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True ‘ 大文字・小文字を区別する場合
.MatchWholeWord = True ‘ 単語単位で一致させる場合
End With

‘ 4. ヒットごとの個別走査と除外判定ループ
Dim replacementCount As Long
replacementCount = 0

Do While fnd.Execute
‘ ヒットした瞬間のRangeオブジェクトを退避
Dim rngFound As Range
Set rngFound = rngTarget.Duplicate

‘ 除外リストとの照合
If Not IsMatchExclusionList(rngFound, exclusionList) Then
‘ 除外対象でなければ置換を実行
rngFound.Text = REPLACE_STRING
replacementCount = replacementCount + 1

‘ 置換後の文字列の末尾にレンジを移動し、無限ループを防止
rngTarget.Start = rngFound.End
Else
‘ 除外対象の場合は、ヒットした単語の次から検索を再開
rngTarget.Start = rngFound.End
End If

‘ レンジの終端をドキュメント末尾に再設定
rngTarget.End = ActiveDocument.Content.End
Loop

‘ 正常終了ログ
MsgBox “置換処理が完了しました。” & vbCrLf & _
“置換実行回数: ” & replacementCount & “件”, vbInformation, “完了”

CleanUp:
‘ 画面描画と警告を復元
With Application
.ScreenUpdating = True
.DisplayAlerts = wdAlertsAll
End With
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

‘ ==============================================================================
‘ 関数名: IsMatchExclusionList
‘ 概要: ヒットしたRangeの文字列、またはその周辺文脈が除外リストに含まれるか判定
‘ ==============================================================================
Private Function IsMatchExclusionList(ByVal rng As Range, ByRef exList() As String) As Boolean
Dim i As Long
Dim checkText As String

‘ 基本はヒットしたテキスト自体を評価
checkText = rng.Text

‘ 【拡張ポイント】必要に応じて、前後数文字を含めた文脈(Context)をチェック対象にする
‘ 例: rng.Expand wdSentence 等で文単位や行単位でのチェックも可能

For i = LBound(exList) To UBound(exList)
‘ 前後の空白をトリムして比較
If Trim(checkText) = Trim(exList(i)) Then
IsMatchExclusionList = True
Exit Function
End If
Next i

IsMatchExclusionList = False
End Function

—

4. コードの急所:なぜこの実装が「プロフェッショナル」なのか

① `rngTarget.Duplicate` による状態の完全分離

Word VBAの `Find` メソッドは、実行されると `Range` 自体の範囲を「ヒットした文字列の範囲」に書き換えてしまう。この挙動を知らないと、ループの継続条件がバグを生むか、無限ループに陥る。
本コードでは、`rngTarget.Duplicate` を用いて、検索用レンジの状態を安全にクローン・退避させながら、ドキュメントを前進させている。

② 無限ループの物理的封鎖

除外条件にヒットした際、`rngTarget.Start = rngFound.End` によって検索開始位置を「今回ヒットした単語の直後」に強制移動させている。これを怠ると、同じ「除外ワード」を永遠に検出し続ける無限ループの地獄を見る。この一手間がプロダクションコードの信頼性を担保する。

③ 拡張性を持たせたアーキテクチャ

除外リストの判定ロジックを独立した関数 (`IsMatchExclusionList`) に切り出している点に注目してほしい。
実務では、「完全一致」だけでなく、「大文字小文字を無視する」「除外キーワードが前方・後方に含まれている部分一致を弾く」といった要件の変更が必ず発生する。この設計であれば、関数内部の比較ロジックを改修するだけで、メインエンジンのコードに手を加える必要がない。

—

5. 運用上の注意点とさらなる高みへ

このエンジンを実際の業務システム(データベース連携や大量のWordファイル群の一括処理)に組み込む際の知見を共有する。

  • データベースや外部ファイル(CSV/JSON)からの除外リスト動的ロード

ハードコーディングされている `Split(“…”, “,”)` の部分を、外部のマスターデータや設定ファイルから読み込むように拡張せよ。これにより、業務部門の担当者がVBAコードを変更することなく、Excelやマスタ画面から除外キーワードを追加・削除できるようになる。

  • トランザクション管理

大規模なドキュメントの置換を行う際は、処理の冒頭で自動バックアップを生成するか、`ActiveDocument.Save` のタイミングを制御すること。誤った除外リストで全置換が走り、保存されてしまった場合のリカバリコストは計り知れない。

「動けばいい」というレベルのコードから脱却し、予測不能な例外をロジックでねじ伏せる――これこそが、業務自動化エンジニアのプライドである。あなたの現場でも、この「守りの置換エンジン」を配備し、真の自動化の果実を味わってほしい。

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