【テクニカル・上級編】【中級者向け】置換対象が「表」の中にある場合、セル単位で色付けする – Word VBA解析バイブル

スポンサーリンク

【Word VBA極限の知見】表のセル内検索と背景色ハイライトの非同期・高パフォーマンス実装

Word VBAにおける`Find`および`Replacement`オブジェクトは、GUIの検索・置換ダイアログのラッパーとして設計されている。そのため、安易な実装はWordの画面描画(スクリーン・アップデート)を伴い、処理速度を致命的に低下させる。

特に「表(Table)の内部」を対象とする場合、セル構造の走査とレンジの再定義が頻発し、O(N^2)に近いパフォーマンス劣化を引き起こす。

本稿では、シニアエンジニアおよび大規模文書を扱うシステム管理者のために、表内の特定文字列を高速に検出し、セル単位で背景色をハッキング的に、かつ安定してハイライトする極限のテクニックを解説する。

—

1. Word VBAの検索・置換における致命的な罠

多くの開発者は、`Selection.Find`を使用するか、あるいは以下のようなナイーブなループを書く。

‘ 【アンチパターン】絶対にやってはいけない実装
Dim c As Cell
For Each c In ActiveDocument.Tables(1).Range.Cells
If InStr(c.Range.Text, “NGワード”) > 0 Then
c.Shading.BackgroundPatternColor = wdColorYellow
End If
Next c

このコードの何が問題か。
1. `Range.Text`のオーバーヘッド: セルの末尾には必ずセル終端文字(Unicode `0x0007`)が含まれており、文字列比較のたびにメモリ割り込みが発生する。
2. `For Each c In Cells`の非効率性: 表がネストしている場合やセル結合(Merge)が存在する場合、ランタイムエラー(エラー 5991: 「このメソッドまたはプロパティは、結合されたセルには使用できません」)を容赦なく吐き出す。
3. 描画のちらつき: スクリーン更新抑制を行わない場合、WordのGUIが毎回再描画を試み、数千セルのドキュメントではフリーズ状態に陥る。

—

2. 結合セルを完全耐性化するセル・アドレス解決とFindの組合せ

結合セルを含む表において、`Range.Cells`を直接叩くのは自殺行為である。正確には、「ヒットしたRangeオブジェクトから親Cellオブジェクトを逆引きする」アプローチを取らなければならない。

`Range.Information(wdWithInTable)` および `Range.Cells(1)` を用いることで、検索ヒット位置から安全にセルを特定できる。

実装アーキテクチャの要件

  • 完全な環境固定: 処理開始前に `ScreenUpdating` と `DisplayAlerts` を無効化。
  • メモリの明示的解放: ループ内で生成されるRangeオブジェクトの残骸を最小化。
  • 結合セル例外のハンドリング: エラー 5991 のバリアフリー化。

—

3. 実用コード:高速セル背景色ハイライト・エンジン

以下のコードは、指定された文書内の全テーブルを対象に、特定のキーワードを検索し、ヒットしたキーワードが含まれるセル全体の背景色を変更する実用的なプロシージャである。

Option Explicit

‘ ==============================================================================
ニッチかつ最高峰のパフォーマンスを誇るWord表内セルハイライト・エンジン
‘ ==============================================================================
Public Sub HighlightTargetCellsInTables(ByVal targetKeyword As String, ByVal highlightColor As WdColorIndex)
‘ 1. 実行環境の極限最適化(描画停止、警告抑制、カーソル固定)
With Application
.ScreenUpdating = False
.DisplayAlerts = wdAlertsNone
.OptimumMemoryUsage = True
End With

Dim doc As Document
Set doc = ActiveDocument

‘ テーブルが存在しない場合は即座にリターン
If doc.Tables.Count = 0 Then
Call RestoreEnvironment
Exit Sub
End If

Dim rngSearch As Range
Dim targetCell As Cell
Dim foundCount As Long
foundCount = 0

‘ 2. ドキュメント全体をカバーする検索レンジの初期化
Set rngSearch = doc.Content

With rngSearch.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = targetKeyword
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False

‘ 3. 高速ループによる検索とセル取得
Do While .Execute
‘ 検索ヒット位置が「表の中」にあるか厳密に判定
If rngSearch.Information(wdWithInTable) Then
On Error Resume Next
‘ ヒットしたレンジが属するセルを取得(結合セル対策としてCells(1)を使用)
Set targetCell = rngSearch.Cells(1)

If Err.Number = 0 And Not targetCell Is Nothing Then
‘ セルの背景色を変更(Shadingオブジェクトの活用)
targetCell.Shading.BackgroundPatternColorIndex = highlightColor
foundCount = foundCount + 1
Set targetCell = Nothing
End If
On Error GoTo 0
End If

‘ 無限ループ防止と次へのポインタ移動:
‘ ヒットしたレンジの末尾に移動して検索を継続
rngSearch.Collapse wdCollapseEnd
Loop
End With

‘ 4. 終了処理とメモリクリーンアップ
Call RestoreEnvironment

MsgBox “処理が完了しました。” & vbCrLf & _
“対象キーワード: ” & targetKeyword & vbCrLf & _
“着色されたセル数: ” & foundCount, vbInformation, “Architect Engine”
End Sub

‘ ==============================================================================
‘ 環境復元プロシージャ
‘ ==============================================================================
Private Sub RestoreEnvironment()
With Application
.ScreenUpdating = True
.DisplayAlerts = wdAlertsAll
End With
End Sub

—

4. このコードが「プロフェッショナル」たる理由(深掘り解説)

1. `WdColorIndex` と `BackgroundPatternColor` の使い分け

コード内では簡便のために `.BackgroundPatternColorIndex = highlightColor`(標準の8色等)を使用しているが、RGB値で厳密なブランドカラーを指定したい場合は以下のように書き換えることも可能だ。

targetCell.Shading.BackgroundPatternColor = RGB(255, 230, 230) ‘ 薄いレッド

`BackgroundPatternColor` はRGB値(Long型)を受け付けるため、システムの要件定義書通りの厳密なカラーリングを実現できる。

2. `rngSearch.Collapse wdCollapseEnd` の重要性

`Do While .Execute` の中で `.Collapse wdCollapseEnd` を行わないと、同じキーワードを無限に検出し続ける(無限ループ)というVBA初心者が陥る典型的なバグが発生する。
検索がヒットした直後のレンジは「ヒットした文字列全体」を選択状態にしているため、その末尾へレンジを縮小(Collapse)させてから次の検索を走らせる必要がある。これがレガシーなWord VBAにおける正しいイディオムである。

3. エラーハンドリングの要塞化

`On Error Resume Next` と `Err.Number = 0` の挟み込みは、悪名高いWordの「結合セル・入れ子セルにおけるオブジェクト不正参照エラー」を完全に無力化する。仮に特定の複雑なテーブル構造でセルの取得に失敗しても、マクロ全体が停止することなく、次の検索ヒットへ処理が流れる耐障害性を担保している。

—

総括

Word VBAにおける表操作と検索の融合は、オブジェクトモデルの挙動を完全に理解していなければ、すぐにパフォーマンスのボトルネックとなる。

今回提示したアプローチは、GUIをバイパスし、メモリ上のRange操作と厳密なエラーハンドリングによって、数万行規模の巨大な仕様書や契約書であっても一瞬で処理を完結させる。

実務の現場において、「動けばいいコード」から「スケールするコード」へ脱皮するための決定版として、ぜひアーキテクチャに組み込んでほしい。

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