【テクニカル・上級編】【中級者向け】ヘッダー・フッター内の文字列も漏らさず検索・置換するループ処理 – Word VBA解析バイブル

スポンサーリンク

【Word VBA極限の知見】ヘッダー・フッターを制す者は文書全体を制す:完全走査とメモリ最適化のアーキテクチャ

Word VBAにおける`Find`および`Replacement`オブジェクトは、一見するとシンプルに見える。だが、実務の現場でその挙動に泣かされた者は数知れない。「本文の置換は完璧に終わったのに、ヘッダーやフッターの機密情報やバージョン表記がそのまま残っていた」――これは、文書自動化における典型的な地雷である。

Wordの構造を深く知る者にとって、本文(Story)とヘッダー/フッター(Header/Footer)は別個のストーリーレンジ(StoryRanges)として存在することは常識だ。しかし、それを「漏れなく、かつ高速に、メモリリークを起こさず」処理し切るコードを書けるエンジニアはどれほどいるだろうか。

本稿では、レガシーなWord文書構造の闇を暴き、セクションごとのリンケージを考慮した上で、文書全体のヘッダー・フッターを完全に走査・置換する極限のVBAアーキテクチャを提示する。

1. Wordストーリーアーキテクチャの真実

Wordの文書(Document)は、単一のテキスト流し込みキャンバスではない。内部的には複数の「ストーリー(Story)」という独立したテキスト空間の集合体として構築されている。

  • wdMainTextStory: メイン本文
  • wdPrimaryHeaderStory / wdFirstPageHeaderStory / wdEvenPagesHeaderStory: 各種ヘッダー
  • wdPrimaryFooterStory / wdFirstPageFooterStory / wdEvenPagesFooterStory: 各種フッター
  • その他、脚注やコメントなど

さらに厄介なのは、文書が複数の「セクション(Section)」に分割されている場合だ。「前と同じヘッダー(LinkToPrevious)」が無効化されているセクションが存在すると、ストーリーレンジを単純に巡回するだけでは、すべてのヘッダーを網羅できない、あるいは重複置換・思わぬ書き換えが発生する。

この迷宮を確実に攻略するには、「文書内の全セクション」を起点とし、それぞれのセクションが持つヘッダー・フッターのストーリーコレクションを網羅的に叩くアプローチが必要不可欠である。

2. 実装:完全走査・置換エンジンのコード

以下に提供するコードは、単なるループ処理ではない。オブジェクトのライフサイクルを意識し、画面描画の抑制によるパフォーマンスの極限化と、エラーハンドリングを網羅したプロダクションクオリティのコードである。

Option Explicit

‘ ==============================================================================
‘ 処理名 : 全セクション・全ヘッダーフッター完全置換エンジン
‘ 概要 : 本文に加え、全セクションのヘッダー・フッターを漏れなく走査し置換する
‘ ==============================================================================
Public Sub ExecuteCompleteSearchAndReplace()
Dim startTime As Double
startTime = Timer

‘ 1. パフォーマンス極限化のための環境設定
Call OptimizeEnvironment(True)

On Error GoTo ErrorHandler

Dim targetDoc As Document
Set targetDoc = ActiveDocument

Dim targetStr As String
Dim replaceStr As String
targetStr = “【社外秘】”
replaceStr = “【Confidential】”

Dim processedCount As Long
processedCount = 0

‘ 2. メイン本文(MainTextStory)の置換
processedCount = processedCount + ReplaceInStory(targetDoc.Content, targetStr, replaceStr)

‘ 3. セクション別 ヘッダー・フッターの網羅的走査
Dim sec As Section
Dim hf As HeaderFooter

For Each sec In targetDoc.Sections
‘ ヘッダーの走査(奇数ページ、偶数ページ、表紙 1ページ目)
For Each hf In sec.Headers
If hf.Exists Then
processedCount = processedCount + ReplaceInStory(hf.Range, targetStr, replaceStr)
End If
Next hf

‘ フッターの走査(奇数ページ、偶数ページ、表紙 1ページ目)
For Each hf In sec.Footers
If hf.Exists Then
processedCount = processedCount + ReplaceInStory(hf.Range, targetStr, replaceStr)
End If
Next hf
Next sec

‘ 4. 処理完了の通知
MsgBox “置換処理が完了しました。” & vbCrLf & _
“総置換回数: ” & processedCount & ” 箇所” & vbCrLf & _
“処理時間: ” & Format(Timer – startTime, “0.00秒”), vbInformation, “アーキテクチャ実行完了”

CleanUp:
‘ 環境の復元
Call OptimizeEnvironment(False)
Exit Sub

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

‘ ==============================================================================
実体としての置換処理を行うカプセル化関数
‘ ==============================================================================
Private Function ReplaceInStory(ByRef targetRange As Range, ByVal findText As String, ByVal repText As String) As Long
Dim count As Long
count = 0

With targetRange.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = findText
.Replacement.Text = repText
.Forward = True
.Wrap = wdFindStop ‘ ストーリーの境界で停止させる(ループ暴走防止)
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False

‘ 実行と置換数のカウント
.Execute Replace:=wdReplaceAll

‘ Word VBAのFind.Executeは返り値で厳密な置換数を返さないため、
‘ 必要に応じて範囲内の再検索やカウントロジックを挟むが、
‘ 今回はシンプルに実行ステータスを担保する設計とする
If .Found Then count = 1 ‘ 簡易的なヒットフラグ(厳密なカウントが必要な場合はループ処理を推奨)
End With

ReplaceInStory = count
End Function

‘ ==============================================================================
‘ パフォーマンス最適化・メモリ負荷軽減のための環境制御
‘ ==============================================================================
Private Sub OptimizeEnvironment(ByVal isEnable As Boolean)
With Application
If isEnable Then
.ScreenUpdating = False
.DisplayAlerts = wdAlertsNone
.Calculation = wdCalculationManual
Else
.ScreenUpdating = True
.DisplayAlerts = wdAlertsAll
.Calculation = wdCalculationAutomatic
End If
End With
End Sub

3. チーフアーキテクトが解説するコードの急所

このコードが一般的な「ネットのサンプルコード」と一線を画す理由は以下の3点にある。

① `wdFindStop` による無限ループの完全阻止

`Find` オブジェクトの `Wrap` プロパティに `wdFindContinue` を指定すると、ストーリーの終端に達した際に文書の先頭に戻り、条件によっては無限ループの罠に落ちる。特にヘッダーやフッターのような微小なテキスト領域では、VBAがクラッシュする原因の筆頭格である。`wdFindStop` を明示し、1つのストーリーレンジ内での走査を1回で完結させるのが鉄則だ。

② セクションごとの `Headers` / `Footers` コレクションの全網羅

Word文書には「先頭ページのみ別指定(`DifferentFirstPageHeaderFooter`)」や「奇数偶数ページ別指定」が存在する。セクションオブジェクトの `sec.Headers` および `sec.Footers` をループさせることで、文書内に隠されたすべての変異状態のヘッダー・フッターにアクセスできる。`.Exists` プロパティによる存在確認を挟むことで、存在しないヘッダーオブジェクトへの無駄なアクセスエラーを回避している。

③ 徹底的なパフォーマンス・オプティマイゼーション

Word VBAが遅い最大の理由は、DOM(Document Object Model)操作のたびにGUIの描画(ScreenUpdating)や再計算(Calculation)が走るからだ。
`OptimizeEnvironment` プロシージャにより、これを完全にシャットダウンする。数千ページの巨大文書であっても、メモリ上で爆速かつ安全に処理を完了させることが可能となる。

4. ささらなる高みへ:システム間連携と大規模運用

このVBAマクロを単体で動かすだけでは、真のプロフェッショナルとは言えない。
社内システムやRPA(UiPathやPower Automateなど)からこのWord VBAを外部起動する場合、ファイルロックやCOM例外への配慮が不可欠となる。

  • COMオブジェクトの解放: マクロ終了時には必ず `Set targetDoc = Nothing` やセクション変数の解放を行い、メモリリークを防止する。
  • エラーログの外部出力: ユーザーの画面にメッセージボックスを出すのではなく、ファイルシステム(FileSystemObject)経由でJSONやCSVにエラーログを吐き出させる設計に昇華させること。

ヘッダー・フッターという「文書の裏側」に潜む文字列の制御を制した時、あなたの文書自動化スキルは間違いなく一段上のステージに到達する。実戦の現場で、このアーキテクチャを心ゆくまで役立ててほしい。

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