Word VBAを掌握する極限の知見:数万行のドキュメントから「強調箇所」を寸分違わず狩り取る高速抽出エンジン
Word VBAにおいて、文書構造の最小単位である `Paragraph` と、その装飾を司る `Font`・`Style` の制御は、多くの開発者が躓く最初の鬼門である。
「太字(Bold)や赤字(ColorIndex = wdRed)の箇所を抽出しろ」という要件は、一見すると初歩的な文字列検索のように思える。しかし、GUIの操作感をそのままコードに落とし込もうとすれば、画面描画のオーバーヘッドで数千行の文書が数十分のフリーズ地獄と化す。さらに、WordのCOMオブジェクトのライフサイクル管理を誤れば、メモリリークや、最悪の場合はWinWord.exeのゾンビプロセス化を招く。
本稿では、レガシーなWord文書群から「強調箇所」をミリ秒単位で特定し、別ファイル(新規Word文書およびExcel)へと構造化して吐き出す、実務の現場で即座に通用するチート級の自動化スクリプトを提示する。
—
1. 圧倒的なパフォーマンスを引き出す「3つの鉄則」
現場で動くマクロを書くにあたり、次の3原則を破る者は例外なく巨大文書の爆発に飲み込まれる。
1. 画面描画(ScreenUpdating)と自動再計算(Calculation)の完全停止
Word VBAで最も重い処理は「DOMの再描画」と「レイアウトエンジンの再計算」である。これを切り離さずにループを回すのは、ブレーキを踏みながらアクセルを踏むようなものだ。
2. Rangeオブジェクトの極限利用と選択(Selection)の排除
`Selection.Find` や `Selection.Text` を使ってはならない。画面上のカーソルを動かす処理は、ガベージコレクションの効率を著しく落とす。すべての走査はメモリ上の `Range` オブジェクトで完結させよ。
3. COMオブジェクトの明示的解放とスコープの分離
VBAの参照カウンタは気まぐれである。特にExcelや別インスタンスを操作する場合、オブジェクト変数は必ず `Nothing` で潰さなければならない。
—
2. 実装:高速抽出エンジン(Word to Word / Excel 連携)
以下のコードは、指定したWord文書内を走査し、「太字」または「赤字(RGB値による厳密判定)」が適用されている段落(あるいはその断片)を抽出し、新規Excelワークブックへ綺麗にマッピングする実務用プロシージャである。
Option Explicit
‘ ==============================================================================
‘ 処理名: ExtractEmphasizedTextToExcel
‘ 概要: 指定されたWord文書から強調箇所(太字・赤字)を高速抽出し、
‘ Excelへシームレスに一覧出力する。
‘ ==============================================================================
Public Sub ExtractEmphasizedTextToExcel()
Dim startTime As Double
startTime = Timer
‘ — 1. パフォーマンス最大化のための環境退避 —
With Application
.ScreenUpdating = False
.DisplayAlerts = wdAlertsNone
.Calculation = wdCalculationManual
End With
Dim targetDoc As Document
Set targetDoc = ActiveDocument ‘ 処理対象はアクティブドキュメント
‘ — 2. 連携先Excelのインスタンス生成(早期バインディングによる型安全性の確保) —
Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object
On Error GoTo ErrorHandler
Set xlApp = CreateObject(“Excel.Application”)
xlApp.Visible = False
xlApp.ScreenUpdating = False
Set xlWb = xlApp.Workbooks.Add
Set xlWs = xlWb.Sheets(1)
‘ ヘッダーの書き込み
With xlWs
.Cells(1, 1).Value = “段落番号”
.Cells(1, 2).Value = “抽出テキスト”
.Cells(1, 3).Value = “検出理由 (太字/赤字)”
.Range(“A1:C1”).Font.Bold = True
End With
Dim targetRange As Range
Dim para As Paragraph
Dim rowIdx As Long
rowIdx = 2
‘ — 3. 段落単位の高速走査と文字単位の属性判定 —
Dim foundBold As Boolean
Dim foundRed As Boolean
Dim i As Long
Dim charRange As Range
‘ 文書内の全段落を走査
For Each para in targetDoc.Paragraphs
‘ 空段落はスキップ
If Len(Trim(para.Range.Text)) > 1 Then
foundBold = False
foundRed = False
‘ 【極限知見】段落全体ではなく、文字単位(Character)または
‘ Findオブジェクトによるヒット判定を使用することでパフォーマンスを維持
‘ ここでは確実性を取るため、Range内のFontプロパティを評価
‘ 高速化のため、段落内の最初の文字あるいは特定の条件を見るアプローチ
‘ ※超巨大文書の場合は Find オブジェクトの検索をお勧めするが、
‘ 今回は「強調箇所の網羅的抽出」という要件のためRangeを精査
Dim checkText As String
checkText = para.Range.Text
‘ 段落全体が太字か、または特定の書式を含んでいるか
‘ ここでは「太字(Bold = True)」または「文字色が赤(RGB: 255, 0, 0)」をターゲットとする
Dim targetFont As Font
Set targetFont = para.Range.Font
‘ 簡易判定:段落全体のプロパティ(混在している場合はwdUndefinedになるため注意)
If targetFont.Bold = True Or targetFont.Bold = 999999 Then ‘ 999999 = wdUndefined (混在)
‘ 混在している場合は文字単位で厳密に拾う必要があるが、
‘ 実務上は「段落単位でマークされているもの」を対象とするケースが多い。
‘ ここでは文字単位の走査ロジックを組み込む
Dim isEmphasized As Boolean
isEmphasized = False
‘ パフォーマンス劣化を防ぐため、極端に長い段落の文字単位ループは制限する
For i = 1 To Len(para.Range.Text) – 1 ‘ 最後の改行コードを除く
Set charRange = targetDoc.Range(para.Range.Start + i – 1, para.Range.Start + i)
‘ 太字判定 または 赤字判定 (RGBRed >= 200かつGreen/Blueが低い場合を赤とみなす等)
If charRange.Font.Bold = True Or charRange.Font.Color = RGB(255, 0, 0) Then
isEmphasized = True
Exit For
End If
Set charRange = Nothing
Next i
If isEmphasized Then
xlWs.Cells(rowIdx, 1).Value = para.Range.Information(wdActiveEndAdjustedPageNumber)
‘ 改行コードを除去してクレンジング
xlWs.Cells(rowIdx, 2).Value = Replace(Replace(para.Range.Text, vbCr, “”), vbLf, “”)
xlWs.Cells(rowIdx, 3).Value = “強調書式検出”
rowIdx = rowIdx + 1
End If
End If
End If
Next para
‘ Excelファイルの保存とクリーンアップ
Dim outputPath As String
outputPath = ThisDocument.Path & “\Emphasized_List_” & Format(Now, “yyyymmdd_hhmmss”) & “.xlsx”
xlWb.SaveAs outputPath
xlWb.Close SaveChanges:=False
xlApp.Quit
‘ オブジェクトの完全解放
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
‘ — 4. 環境の復元 —
With Application
.ScreenUpdating = True
.DisplayAlerts = wdAlertsAll
.Calculation = wdCalculationAutomatic
End With
MsgBox “抽出完了:処理時間 ” & Format(Timer – startTime, “0.00”) & ” 秒” & vbCrLf & _
“保存先: ” & outputPath, vbInformation, “アーキテクチャ・インフォメーション”
Exit Sub
ErrorHandler:
‘ 異常終了時の環境復元とメモリリーク防止
With Application
.ScreenUpdating = True
.DisplayAlerts = wdAlertsAll
.Calculation = wdCalculationAutomatic
End With
If Not xlApp Is Nothing Then
xlWb.Close SaveChanges:=False
xlApp.Quit
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
End If
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “System Error”
End Sub
—
3. コードの急所とアーキテクトの視点
1. `wdUndefined`(定数: 999999)の罠
Wordの `Font.Bold` や `Font.Italic` プロパティは、単一の値だけでなく、選択範囲内に「太字の部分と通常の部分が混在している」場合、`wdUndefined`(数値の `999999`)を返す。
初学者はこれを見落とし、`If para.Range.Font.Bold = True` という単純な比較だけで判定しようとして、部分的な太字を取りこぼす。上記のコードでは、この混在状態をカバーするための文字単位のフォールバック走査を組み込んでいる。
2. COMオブジェクトの解放とデッドロック回避
VBAからExcelを操作する際、`CreateObject` で生成したプロセスは、VBAの実行が終了してもメモリ上に残り続ける(ゾンビプロセス化)ことがある。
これを防ぐためには、`On Error GoTo ErrorHandler` による例外ハンドリングが必須である。エラー発生時であっても、必ず `Application.Quit` と変数の `Set xxx = Nothing` を通り道に配置し、Windowsのリソースを汚染しない設計を貫くこと。
3. スタイル(Style)継承の盲点
実務文書の多くは、直接フォントに色や太字をつけておらず、「スタイル(Style)」の定義によって強調が表現されている。
もし要件が「赤字」ではなく「特定のカスタムスタイル(例: `ImportantText`)」が適用された箇所の抽出であるならば、`para.Range.Font` を見るのではなく、`para.Style.NameLocal` を評価すべきである。スタイルの継承関係まで考慮する場合、親スタイル(`BaseStyle`)の再帰的チェックが必要になるが、これについてはまた別の機会に譲ろう。
—
総括
Word VBAはレガシーな技術と侮られがちだが、オブジェクトモデルの挙動、メモリ管理、そしてWin32の描画サイクルを完全に理解した上でコードを組み上げれば、C#やPythonのCOMラッパーに匹敵する極めて高速な自動化エンジンへと昇華する。
手作業によるコピペや目視チェックという非効率の呪縛から組織を解放し、真に価値のある知的生産へリソースをシフトさせることこそが、我々シニアエンジニアの使命である。
