Word VBAで「差分検知」を極める:プロフェッショナルな文書比較エンジンの設計思想
Word VBAで「2つの文書を比較したい」と考えたとき、多くのエンジニアが陥る罠がある。それは、`Selection`オブジェクトを多用した力技のループ処理だ。画面を激しく点滅させ、メモリを浪費し、挙句の果てに「比較元の文書が破損した」という悲劇を招く。
本稿では、Wordのネイティブな `Compare` メソッドを核とし、`Range` オブジェクトを制御することで、堅牢かつ高速な差分抽出エンジンを構築する極意を伝授する。
—
1. なぜ「力技の比較」は失敗するのか
Word VBAにおいて、`Selection`は極めて低速なオブジェクトだ。UIと同期するため、比較対象が数十ページに及べば、処理時間は指数関数的に増大する。
我々が目指すべきは、「Wordエンジンを裏側で回し、メモリ空間上のRangeオブジェクトを操作する」ことだ。Wordには強力な `Document.Compare` メソッドが備わっている。これを適切にラップし、差分を「抽出」して「レポート」に変換する設計こそが、プロのプロダクションコードである。
—
2. 堅牢な比較エンジンのアーキテクチャ
設計の肝は、「比較用の一時文書(Temporary Document)を生成し、そこから差分情報を吸い上げる」という構造だ。
1. 参照整合性の確保: 元の文書は決して書き換えない。`Compare` メソッドで生成された「比較結果文書」のみを操作対象とする。
2. Rangeによるメモリ管理: `Revisions`(変更履歴)コレクションを走査し、必要な箇所だけを特定する。
3. エラーハンドリング: `Application.ScreenUpdating = False` は必須。これなしに大規模処理を行うのはプロとして失格だ。
—
3. 実装コード:差分抽出エンジン
以下のコードは、指定した2つのファイルから差分を特定し、新しいレポートドキュメントに抽出するテンプレートだ。
Option Explicit
‘ ———————————————————
‘ 文書比較のメイン実行ユニット
‘ ———————————————————
Public Sub GenerateDifferenceReport(originalPath As String, modifiedPath As String)
Dim docOrig As Document, docMod As Document, docReport As Document
Dim docDiff As Document
‘ パフォーマンス向上のための定石
Application.ScreenUpdating = False
Set docOrig = Documents.Open(originalPath, Visible:=False)
Set docMod = Documents.Open(modifiedPath, Visible:=False)
‘ 比較を実行(比較結果が新しい文書として作成される)
Set docDiff = Application.CompareDocuments( _
OriginalDocument:=docOrig, _
RevisedDocument:=docMod, _
Destination:=wdCompareDestinationNew)
‘ レポート用文書の準備
Set docReport = Documents.Add
‘ 差分(Revisions)を抽出してレポートへ転記
Call ExtractRevisions(docDiff, docReport)
‘ クリーンアップ
docOrig.Close SaveChanges:=False
docMod.Close SaveChanges:=False
docDiff.Close SaveChanges:=False
Application.ScreenUpdating = True
MsgBox “レポートの作成が完了しました。”, vbInformation
End Sub
‘ ———————————————————
‘ 変更履歴(Revisions)を解析し、整形して抽出するロジック
‘ ———————————————————
Private Sub ExtractRevisions(sourceDoc As Document, targetDoc As Document)
Dim rev As Revision
Dim i As Long
targetDoc.Content.InsertAfter “■ 差分抽出レポート” & vbCrLf & vbCrLf
‘ RevisionsコレクションはRangeオブジェクトの集合体として扱う
For Each rev In sourceDoc.Revisions
With targetDoc.Range(targetDoc.Content.End – 1, targetDoc.Content.End – 1)
.InsertAfter “変更箇所: ” & rev.Range.Text & vbCrLf
.InsertAfter “変更タイプ: ” & GetRevType(rev.Type) & vbCrLf
.InsertAfter “————————–” & vbCrLf
End With
Next rev
End Sub
Private Function GetRevType(revType As WdRevisionType) As String
Select Case revType
Case wdRevisionInsert: GetRevType = “挿入”
Case wdRevisionDelete: GetRevType = “削除”
Case Else: GetRevType = “その他”
End Select
End Function
—
4. プロの現場で不可欠な3つの知見
① データベース連携時の注意点
差分情報をDB(SQL ServerやSQLite)に格納する場合、`Range.Text` をそのまま突っ込んではならない。Word特有の制御文字(段落記号やセクション区切り)が混入し、データが汚染される。抽出時には `Replace(rev.Range.Text, vbCr, “”)` 等で正規化する処理を必ず挟むこと。
② 大規模文書のメモリリーク防止
`Application.CompareDocuments` を繰り返すと、Wordのセッション内に不可視のドキュメントが残存しやすい。必ず `Set` したオブジェクトは `Nothing` を明示し、`Close` を徹底すること。これを怠ると、長時間稼働するExcel/Wordマクロは確実にメモリ不足(OOM)でクラッシュする。
③ ユーザー体験(UX)の最適化
もしこのツールを他のユーザーに配布するなら、`Application.StatusBar` を活用せよ。
`Application.StatusBar = “比較処理中… ” & i & “件目の差分を解析中”`
このように進捗を可視化するだけで、ツールに対する信頼性と安心感が劇的に変わる。
—
結びに代えて
VBAは「レガシーな言語」と揶揄されることもある。しかし、Wordという巨大なGUIを持つ文書エンジンの心臓部を直接叩けるのは、VBAをおいて他にない。
「動くコード」を書くのは初級者だ。
「壊れないコード」を書き、「リソースを最適化し」、「保守可能な設計」を貫く。それが我々、業務自動化エンジニアの誇りである。この比較ロジックをベースに、貴方の現場の業務を極限まで効率化してほしい。
