【Word VBA】フィールドコード破壊という「惨劇」を回避する。検索・置換の設計思想
業務自動化エンジニアとして多くの現場を見てきたが、Word VBAで最も多くの「悲劇」を生んでいるのが、無思慮な`Find.Execute`だ。
「特定のキーワードを全置換する」という単純なタスク。しかし、その裏で目次(TOC)や差し込み印刷のフィールドコードが破壊され、ドキュメントの整合性が崩壊した瞬間を、君は見たことがあるか? 私は何度も見てきた。そして、その修復に費やされる膨大な残業代も。
今日は、Wordの内部構造を理解した上で、「フィールドコードを壊さずに検索・置換を完遂する」ための、実務レベルの極限ロジックを伝授する。
—
1. なぜ「全置換」は地雷なのか?
Wordの`Range.Find`は、デフォルトでは「文書内のテキスト」だけでなく「フィールドコードの内部文字列」までをも検索対象に含める。
例えば、`{ REF MyTarget \ MERGEFORMAT }` というフィールドがあったとする。ここで「My」を検索し、別の文字列に置換してしまったらどうなるか? フィールドコードの構造は即座に崩壊し、Wordは「構文エラー」を吐く。あるいは、差し込み印刷のデータリンクが切断される。
「選択範囲全体を検索する」というアプローチは、Wordにおいては無防備な自殺行為に等しい。
—
2. フィールドコードを「安全圏」に隔離する戦略
フィールドコードを汚染から守る唯一の方法は、「置換処理の直前に、対象がフィールドコード内にあるかを判定し、除外する」というフィルタリング機構を組み込むことだ。
VBAには `Range.Information(wdInsideField)` という強力なプロパティがある。これを使えば、現在のRangeがフィールド内にあるかどうかを一瞬で判定できる。しかし、単純にループで判定するだけでは遅すぎる。ドキュメントが長大になればなるほど、パフォーマンスは死ぬ。
ここで、伝説的なアーキテクトが用いる「構造的検索」のコードを公開する。
—
3. 実践:プロダクションレベルの「堅牢な置換ルーチン」
以下のコードは、単なる検索ではなく、フィールドコードを回避するための「安全装置」を内蔵している。
‘ —————————————————————————
‘ @brief 文書内から特定の文字列を置換する。フィールドコード内は無視する。
‘ @param findText 検索対象文字列
‘ @param replaceText 置換後の文字列
‘ —————————————————————————
Public Sub SafeReplace(findText As String, replaceText As String)
Dim rng As Range
Set rng = ActiveDocument.Content
‘ 検索の初期化
With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = findText
.Replacement.Text = replaceText
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
End With
‘ 検索ループの開始
Do While rng.Find.Execute
‘ 【核心】対象がフィールド内にあるか判定
‘ wdInsideField が True ならば、その検索結果は無視して次へ進む
If Not rng.Information(wdInsideField) Then
‘ 置換を実行する
rng.Text = replaceText
‘ 置換後にRangeを移動させ、無限ループを防止
rng.Collapse wdCollapseEnd
Else
‘ フィールド内ならスキップし、検索範囲を一つ進める
rng.Collapse wdCollapseEnd
End If
Loop
End Sub
なぜこのコードが「最強」なのか?
1. `rng.Collapse wdCollapseEnd` の重要性:
置換後に`Range`を適切に再配置しないと、置換後の文字列が再度検索対象となり、無限ループが発生する。この制御こそがプロの証明だ。
2. `Information(wdInsideField)`の活用:
このメソッドはWordの内部オブジェクトモデルに直接アクセスする。ループ内で重い処理を行わず、条件判定のみに留めることで、数万文字の文書でも実用的な速度を維持する。
3. 保守性:
検索オプションを`With`ブロックで分離しているため、正規表現やワイルドカード検索への変更も容易だ。
—
4. さらに高度な自動化を目指す君へ
もし、君が扱う文書がデータベースと連携しており、頻繁に大規模な置換を行っているなら、さらなる高みへ行こう。
- ワイルドカード検索の罠: ワイルドカード(`MatchWildcards:=True`)を使用する場合、検索結果のRangeが不意に大きく(あるいは小さく)なることがある。その際、`Information`判定が外れるケースがあるため、必ず `rng.Start` と `rng.End` がフィールドコードの境界を跨いでいないかチェックするロジックを追加せよ。
- イベント駆動の検討: ドキュメントのプロパティを更新した直後に置換を行いたい場合は、`Document_ContentControlOnExit` などのイベントと組み合わせることで、ユーザーが編集を終えた瞬間に「汚染のない」置換をトリガーできる。
—
最後に:エンジニアの誇りとして
「とりあえず動く」コードを書くのは新人だ。
「壊れないコード」を書くのが中級者。
そして、「変化を予見し、システムの整合性を守り抜くアーキテクチャ」を書くのが、真のエンジニアだ。
Word VBAはレガシーと言われることもあるが、その奥にあるオブジェクトのライフサイクルは、今なお極めて精巧だ。このコードを君のツールキットに加え、二度と「目次が壊れました」という悲鳴を聞かない現場を作ってほしい。
健闘を祈る。
