【Word VBA極限攻略】「Find」の死角を突く:StoryRanges再帰巡回による完全置換アーキテクチャ
Word VBAで「検索・置換」を実装する際、多くの初学者が陥る罠がある。`ActiveDocument.Content.Find` で満足し、ドキュメントの「ほんの一部」しか舐めていないことに気づかないまま、納品後に「図形の中の文字が置換されていない!」という致命的なバグ報告を受けるのだ。
Wordのドキュメント構造は、単一のストリームではない。本文(MainTextStory)の裏には、テキストボックス、脚注、ヘッダー、フッターといった「Story(物語)」が独立して存在している。これらを網羅的に制御できないエンジニアは、Word自動化のスタートラインにすら立っていないと言っていい。
本稿では、Wordの内部構造を掌握し、いかなる場所であっても漏れなく置換を完遂する「堅牢な巡回ロジック」を伝授する。
—
1. なぜ `ActiveDocument.Content` だけでは不十分なのか
Wordのドキュメントは、`StoryRanges` というコレクションによって多層的に管理されている。
`ActiveDocument.Content` はあくまでメインの本文領域のみを指す。テキストボックス(Shape)や脚注、あるいはヘッダー/フッターは、それぞれが個別の `Range` オブジェクトとして独立しており、メインストリームを検索してもこれらは完全に無視される。
プロフェッショナルな自動化ツールを作るなら、「すべてのStoryRangeを再帰的に走査する」設計が必須である。
—
2. 実践:プロダクションレベルの「全範囲置換エンジン」
以下のコードは、単なる検索置換ではなく、Wordの全レイヤーを網羅的に走査する堅牢な構造を持っている。
Option Explicit
”’
”’
Public Sub ExecuteGlobalReplace(ByVal strFind As String, ByVal strReplace As String)
Dim rngStory As Range
Dim shp As Shape
‘ エラーハンドリング:予期せぬオブジェクト破損に備える
On Error Resume Next
‘ 全てのストーリータイプを巡回する
For Each rngStory In ActiveDocument.StoryRanges
‘ 現在の範囲で検索置換を実行
Call ReplaceInRange(rngStory, strFind, strReplace)
‘ リンクされたストーリー(NextStory)があれば再帰的に処理する
‘ これにより、連結されたテキストボックス等を漏らさず捕捉できる
Do While Not (rngStory.NextStoryRange Is Nothing)
Set rngStory = rngStory.NextStoryRange
Call ReplaceInRange(rngStory, strFind, strReplace)
Loop
Next rngStory
‘ 重要:Shape(図形・テキストボックス)内部の走査
‘ StoryRangesだけでは届かない「浮いている」テキストを処理する
For Each rngStory In ActiveDocument.StoryRanges
If rngStory.ShapeRange.Count > 0 Then
For Each shp In rngStory.ShapeRange
If shp.TextFrame.HasText Then
Call ReplaceInRange(shp.TextFrame.TextRange, strFind, strReplace)
End If
Next shp
End If
Next rngStory
On Error GoTo 0
MsgBox “全領域の置換が完了しました。”, vbInformation
End Sub
Private Sub ReplaceInRange(ByRef rng As Range, ByVal f As String, ByVal r As String)
With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = f
.Replacement.Text = r
.Forward = True
.Wrap = wdFindContinue ‘ 文書末尾まで検索を継続
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = False
.Execute Replace:=wdReplaceAll
End With
End Sub
—
3. この設計が「プロの仕事」である理由
1. `Do While Not rngStory.NextStoryRange Is Nothing` の重要性
Wordの `StoryRanges` は、一見するとリストのように見えるが、実際には連結リスト(Linked List)の構造を持っている。単に `For Each` を回すだけでは、溢れたテキストボックスや連結されたセクションを見落とす。この再帰的な `NextStoryRange` への追跡こそが、漏れを防ぐ唯一の解だ。
2. `ShapeRange` の個別走査
`StoryRanges` を巡回しても、一部の図形オブジェクト内のテキストは検索に引っかからないケースがある。そのため、`ShapeRange` を明示的にループし、`TextFrame` が存在するかをチェックする二段構えの防壁を構築している。
3. パフォーマンスと保守性の最適化
- `.ClearFormatting` の徹底: 検索・置換において最も多いバグは「前回の検索条件(太字設定など)が残っていること」によるヒット漏れだ。毎回リセットを強制することで、安定性を担保する。
- エラーハンドリング: Wordのオブジェクトモデルは、一部の破損した図形や保護されたセクションに触れると簡単にクラッシュする。`On Error Resume Next` で最小限の生存戦略をとりつつ、業務を止めない実装にしている。
—
4. エンジニアへの提言:データベース連携時の注意点
このツールを外部DB(ExcelやSQL Server)と連携させる場合、「置換文字列に含まれる特殊文字」に注意が必要だ。
特に、改行コード(`^p`)やタブ(`^t`)をデータベースから読み込む際は、そのままWordの検索文字列に流し込んでも正しく認識されないことが多い。
データベース側で格納する際は、VBAで認識できる制御文字に置換する前処理(`Replace(strFromDB, “\n”, “^p”)` など)を必ず実装すること。
Word VBAはレガシーな技術と見なされがちだが、そのオブジェクトモデルを深く理解すれば、現代のどのツールよりも強力なドキュメント・オートメーションエンジンとなる。
コードをコピペして終わりではない。なぜこの設計が必要なのか、その「アーキテクチャの背景」を理解した者だけが、真の自動化エンジニアとして評価されるのだ。健闘を祈る。
