【入門編】Word VBAで作成する『文書比較ツール』:2つの文書の差分を色付けする – Word VBA解析バイブル

スポンサーリンク

Word VBAで実現!あなただけの「文書比較ツール」作成入門 ~差分を色付けで一目瞭然に~

Wordの標準機能にも文書比較はありますが、「ちょっと違うな」「もっと細かく制御したいな」と思ったことはありませんか?

実は、Word VBAを使えば、そんなあなたの要望に応える、オリジナルの文書比較ツールを自作できるんです。しかも、その仕組みを理解すれば、Word VBAの基本がグッと身につき、マクロの記録だけでは得られない「生きた知識」が手に入ります。

今回は、Word VBAの「Application」「Document」「Range」といった基本的なオブジェクトを紐解きながら、2つの文書のテキストを比較し、変更箇所を色付けする「文書比較ツール」の作成方法を、コード例と共にご紹介します。

「VBAって難しそう…」と感じている方も、ご安心ください。まるで隣で先輩エンジニアが優しく教えてくれるかのように、一つ一つ丁寧に解説していきますね。ここをクリアすれば、Word VBAの基本はバッチリですよ!

なぜ「自作」なのか? Word VBAの「Application」「Document」「Range」を理解する第一歩

Word VBAで何かを自動化しようとするとき、まず最初に登場するのが Application オブジェクトです。これは、Wordアプリケーションそのものを表す、いわば「親玉」のような存在です。

‘ Wordアプリケーションそのものを指します
Dim wdApp As Application
Set wdApp = Application

この `Application` オブジェクトを通して、私たちはWordの様々な機能にアクセスできます。例えば、開いている文書の一覧を取得したり、新しい文書を作成したり。

次に重要なのが Document オブジェクトです。これは、現在開いている、あるいは操作対象となっている「文書」そのものを表します。

‘ 現在アクティブな文書を指します
Dim wdDoc As Document
Set wdDoc = ActiveDocument

‘ 特定のファイルを開いて、それを指します(ファイルパスは適宜変更してください)
Dim wdDocFromFile As Document
Set wdDocFromFile = Documents.Open(“C:\Users\YourName\Documents\Sample1.docx”)

そして、さらに細かく文書の「部分」を操作するために登場するのが Range オブジェクトです。Rangeは、文書内の特定のテキスト範囲を指定するために使われます。これは、単なる文字の羅列ではなく、開始位置と終了位置を持つ「領域」と考えると分かりやすいでしょう。

‘ 文書全体を指すRange
Dim wdRangeAll As Range
Set wdRangeAll = wdDoc.Content

‘ 段落を指定するRange(例: 最初の段落)
Dim wdRangeParagraph As Range
Set wdRangeParagraph = wdDoc.Paragraphs(1).Range

‘ 特定の文字列を含むRange(例: “比較対象” という文字列)
Dim wdRangeFind As Range
On Error Resume Next ‘ 見つからなかった場合のエラーを回避
Set wdRangeFind = wdDoc.Content.Find.Execute(FindText:=”比較対象”)
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

これらのオブジェクトの関係性を、ピラミッドのようにイメージしてみてください。

Application (Word全体)
|
Document (開いている文書)
|
Range (文書内の特定の範囲)

この「Application → Document → Range」という階層構造を理解することが、Word VBAを使いこなす上での最初の、そして最も重要なステップです。今回の「文書比較ツール」では、このRangeオブジェクトを駆使して、2つの文書のテキストを文字単位で比較していくことになります。

さあ、始めよう! 2つの文書を比較する基本ロジック

Word標準の比較機能は便利ですが、VBAで自作するメリットは、比較のロジックを完全にコントロールできる点にあります。例えば、「大文字・小文字は区別しない」「空白文字は無視する」といった、独自の条件を設定できるようになります。

今回は、最も基本的な「文字単位での完全一致比較」を実装してみましょう。

比較の基本的な流れ

1. 対象となる2つの文書を開く(または、既に開いている文書を指定する)。
2. それぞれの文書のテキストをRangeオブジェクトとして取得する
3. 一方の文書のテキストを、もう一方の文書のテキストと比較していく
4. 一致しない部分(差分)を特定する
5. 特定した差分部分に、色を付けてハイライトする

この「4. 一致しない部分を特定する」部分が、自作比較ツールの肝となります。今回は、比較的シンプルな「一方の文書を基準に、もう一方にない文字や、順番が違う文字」を見つけ出す方法で解説します。

コード例:2つの文書の差分を色付けするVBAマクロ

まずは、実際に動作するVBAコードを見てみましょう。このコードは、アクティブな文書(文書A)と、次に開かれる文書(文書B)を比較し、文書Aに変更があった箇所を赤色でハイライトします。

Sub CompareDocumentsHighlightChanges()

Dim wdApp As Application
Dim wdDocA As Document ‘ 基準となる文書(例: アクティブな文書)
Dim wdDocB As Document ‘ 比較対象の文書(例: 新しく開く文書)
Dim rngA As Range
Dim rngB As Range
Dim strTextA As String
Dim strTextB As String
Dim i As Long ‘ ループカウンター
Dim j As Long ‘ 比較用インデックス

‘ — 初期設定 —
Set wdApp = Application

‘ 基準となる文書を設定 (ここではアクティブな文書を想定)
If ActiveDocument Is Nothing Then
MsgBox “比較対象の文書(文書A)を開いてください。”, vbExclamation
Exit Sub
End If
Set wdDocA = ActiveDocument

‘ 比較対象の文書を開く (ファイルパスは適宜変更してください)
On Error Resume Next ‘ ファイルが見つからなかった場合のエラーを回避
Set wdDocB = Documents.Open(“C:\Path\To\Your\DocumentB.docx”) ‘ ★★★ このパスを実際のファイルパスに変更してください ★★★
On Error GoTo 0

If wdDocB Is Nothing Then
MsgBox “比較対象の文書(文書B)を開けませんでした。パスを確認してください。”, vbExclamation
Exit Sub
End If

‘ 両文書のテキストをRangeオブジェクトとして取得
Set rngA = wdDocA.Content
Set rngB = wdDocB.Content

‘ 比較のために、テキストを文字列として取得(後で変更箇所を特定しやすくするため)
strTextA = rngA.Text
strTextB = rngB.Text

‘ — 差分検出と色付け —

‘ 基準文書(wdDocA)のテキストを1文字ずつ見ていく
‘ ただし、実際にはもっと高度なアルゴリズムが必要になりますが、
‘ ここでは簡略化して、文字の追加・削除・変更を「差分」として捉えます。
‘ より厳密な比較には、Diffアルゴリズム(例: Myers diff algorithm)などを実装する必要があります。

‘ 簡単な例として、文書Aのテキストを文書Bのテキストと比較します。
‘ 文書Bにない文字、または順番が違う文字を「差分」とみなします。
‘ このコードは、あくまで「差分検出の入り口」として、概念を理解するためのものです。

‘ 文書Bのテキストを基準に、文書Aのテキストを走査します。
‘ 文書Aにしか存在しない文字、または文書Bの同じ位置にない文字を差分とします。

Dim currentPosA As Long
Dim currentPosB As Long
currentPosA = 1
currentPosB = 1

‘ 文書Aのテキストが文書Bのテキストより長い場合、末尾の追加部分を差分とする
If Len(strTextA) > Len(strTextB) Then
‘ 文書Aの末尾にある、文書Bにはない部分をハイライト
Dim startHighlight As Long
startHighlight = currentPosB + 1 ‘ 文書Bの最後の文字の次から
If startHighlight <= Len(strTextA) Then ' 文書Aの残りの部分をハイライト Dim rngHighlight As Range Set rngHighlight = wdDocA.Range(startHighlight - 1, Len(strTextA) - 1) ' 0ベースインデックスに注意 rngHighlight.Font.Shading.BackgroundPatternColor = wdColorRed ' 背景色を赤に rngHighlight.Font.Bold = True ' 太字にする(任意) End If End If ' 文書Aと文書Bのテキストを文字単位で比較 For i = 1 To Len(strTextA) ' 文書Aのi番目の文字を取得 Dim charA As String charA = Mid(strTextA, i, 1) ' 対応する文書Bの文字を取得(ただし、範囲外に注意) Dim charB As String If currentPosB <= Len(strTextB) Then charB = Mid(strTextB, currentPosB, 1) Else charB = "" ' 文書Bが短い場合 End If ' 文字が一致しない場合、差分とみなす If charA <> charB Then
‘ 文書Aで差分があった箇所を特定し、色付け
Dim rngDiff As Range
‘ 差分があった文字のRangeを取得(i-1は0ベースインデックスのため)
Set rngDiff = wdDocA.Range(i – 1, i – 1)
rngDiff.Font.Shading.BackgroundPatternColor = wdColorRed ‘ 背景色を赤に
rngDiff.Font.Bold = True ‘ 太字にする(任意)

‘ 文書B側でも同様の処理を行う場合は、ここにコードを追加
‘ 例: Set rngDiffB = wdDocB.Range(currentPosB – 1, currentPosB – 1)
‘ rngDiffB.Font.Shading.BackgroundPatternColor = wdColorBlue ‘ 別の色でハイライト

‘ 文書Bのインデックスを進める(一致しなかった場合も、次の文字へ進める)
currentPosB = currentPosB + 1
Else
‘ 文字が一致した場合、文書Bのインデックスを進める
currentPosB = currentPosB + 1
End If
Next i

‘ — 比較対象文書の保存と終了処理 —
‘ 比較対象文書(wdDocB)は、必要に応じて保存するか、閉じる
‘ wdDocB.Save
wdDocB.Close SaveChanges:=False ‘ 保存せずに閉じる場合

MsgBox “文書比較が完了しました。変更箇所が赤色でハイライトされています。”, vbInformation

End Sub

コードのポイント解説

  • `Dim wdDocA As Document`, `Dim wdDocB As Document`: 比較する2つの文書を格納する変数です。`wdDocA` を「基準」、`wdDocB` を「比較対象」とします。
  • `Set wdDocA = ActiveDocument`: 現在アクティブな文書を `wdDocA` に代入します。
  • `Set wdDocB = Documents.Open(…)`: 指定したパスの文書を `wdDocB` として開きます。このパスはご自身の環境に合わせて必ず変更してください。
  • `Set rngA = wdDocA.Content`, `Set rngB = wdDocB.Content`: それぞれの文書の「内容全体」をRangeオブジェクトとして取得します。
  • `strTextA = rngA.Text`, `strTextB = rngB.Text`: Rangeオブジェクトから、実際のテキストデータを文字列として取り出します。VBAでの文字列操作は、この文字列変数を使うと直感的で分かりやすいです。
  • `For i = 1 To Len(strTextA)`: 文書Aのテキストを、1文字ずつループ処理で見ていきます。
  • `Mid(strTextA, i, 1)`: 文字列 `strTextA` の `i` 番目から1文字を取得します。
  • `If charA <> charB Then … End If`: 文書Aの現在の文字と、文書Bの対応する文字が一致しない場合に、差分と判断します。
  • `Set rngDiff = wdDocA.Range(i – 1, i – 1)`: 差分が見つかった「文字」のRangeオブジェクトを、文書Aから取得します。`i – 1` としているのは、VBAのRangeオブジェクトのインデックスは0から始まるためです。
  • `rngDiff.Font.Shading.BackgroundPatternColor = wdColorRed`: 取得したRangeオブジェクトのフォントの背景色を赤色に設定します。これで、変更箇所がハイライトされます。
  • `currentPosB = currentPosB + 1`: 文字が一致した場合、または一致しなかった場合でも、文書B側の比較位置を進めます。
  • `wdDocB.Close SaveChanges:=False`: 比較が終わったら、比較対象の文書Bは、変更を保存せずに閉じます。

注意点と、より実践的な比較について

上記コードの差分検出ロジックは、非常にシンプルで、概念を理解するためのものです。 実際の文書比較では、以下のような課題が出てきます。

  • 改行コードやタブ文字の扱い: これらをどのように比較対象に含めるか、あるいは無視するかで結果が変わります。
  • 単語単位、文単位での比較: 文字単位ではなく、単語や文で比較した方が、より人間が理解しやすい差分になることがあります。
  • 挿入・削除・置換の区別: 単純な不一致だけでなく、どの種類の変更があったのかを区別したい場合があります。
  • パフォーマンス: 非常に長い文書を比較する場合、文字単位のループ処理は時間がかかる可能性があります。

より高度な差分検出アルゴリズム(例: Myers diff algorithm、Longest Common Subsequence (LCS) アルゴリズムなど)をVBAで実装することも可能ですが、コードが複雑になります。もし、より精密な比較機能が必要な場合は、.NETなどの他の言語で差分検出ライブラリを利用し、COM連携などでWord VBAから呼び出す、といった方法も検討できます。

しかし、まずはこの基本コードで「Rangeオブジェクトを使ってテキストを操作し、条件によって書式を変更する」というVBAの基本をマスターすることが重要です。

陥りやすいエラーとその回避策 ~先輩からのアドバイス~

Word VBAを書いていて、「あれ?うまくいかないぞ?」となることは、誰にでもあります。ここでは、特に初心者が陥りやすいエラーと、その回避策をいくつかご紹介します。

1. オブジェクト変数が「Nothing」のまま!

‘ 例:
Dim wdDoc As Document
‘ Set wdDoc = … の行を書き忘れた、またはエラーで代入できなかった場合
wdDoc.Content.Text = “テスト” ‘ ここでエラー発生!

エラー内容: 「オブジェクト変数またはWithブロック変数が設定されていません。」

解説: 変数にオブジェクト(DocumentやRangeなど)を代入する `Set` ステートメントを忘れていたり、`Set` が実行される前にその変数を使おうとすると発生します。

回避策:

  • 変数を宣言したら、必ず `Set` でオブジェクトを代入してから使うように習慣づけましょう。
  • `If wdDoc Is Nothing Then …` のように、オブジェクトが有効かチェックしてから処理を行うようにします。

2. Rangeオブジェクトのインデックスミス(0ベース vs 1ベース)

‘ 例:
Dim rng As Range
Set rng = ActiveDocument.Content
‘ rng.Start は 0 から始まるが、rng.Characters(1) は 1 から始まる…
‘ 混乱しやすい!

解説: VBAのオブジェクトモデルでは、Rangeの開始位置(`Start`)や終了位置(`End`)は0から始まるインデックスで扱われます。しかし、`Paragraphs(1)` のようにコレクションの要素を指定する場合は1から始まるインデックスを使います。この混同がバグの原因になります。

回避策:

  • 「Rangeの座標は0から、コレクションのインデックスは1から」 と強く意識しましょう。
  • コード内でインデックスを使う際は、コメントで「0ベース」「1ベース」と明記しておくと親切です。
  • `rng.Characters(i)` のように文字を1つずつ取得する場合、`i` が1から始まるループなら `rng.Characters(i)`、`i` が0から始まるループなら `rng.Characters(i + 1)` となる、といった対応を正確に行います。

3. ファイルパスの誤り

‘ 例:
Set wdDocB = Documents.Open(“C:\My Documents\Sample.docx”) ‘ 実際は C:\My_Documents\Sample.docx だった!

解説: `Documents.Open` メソッドなどで指定するファイルパスが間違っていると、ファイルが見つからずエラーになります。

回避策:

  • ファイルパスは、コピー&ペーストを正確に行い、手入力の場合はスペルミスがないか複数回確認しましょう。
  • `On Error Resume Next` と `On Error GoTo 0` を使って、エラー発生時も処理を継続できるようにし、エラーメッセージで原因を特定しやすくします。

4. 比較対象文書の「隠れた」変更

Word文書には、表示されない情報(フィールドコード、コメント、変更履歴など)が含まれていることがあります。これらが比較結果に影響を与える場合があります。

回避策:

  • 比較前に、`ActiveDocument.ActiveWindow.View.ShowHiddenText = False` のように、表示設定を調整することを検討します。
  • より厳密な比較が必要な場合は、テキストだけでなく、書式情報なども含めて比較するロジックを検討する必要がありますが、これは複雑になります。

まとめ:ここから、あなたの「自動化」は加速する!

いかがでしたでしょうか?

今回は、Word VBAの基本的なオブジェクトである `Application`、`Document`、`Range` を理解することから始め、2つの文書のテキストを比較して差分を色付けする、という具体的な「文書比較ツール」の作成方法について解説しました。

  • `Application`: Wordアプリケーションそのもの
  • `Document`: 開いている個々の文書
  • `Range`: 文書内の特定のテキスト範囲

この3つのオブジェクトの関係性をしっかりと掴むことが、Word VBAマスターへの第一歩です。そして、Rangeオブジェクトを駆使してテキストを操作し、`If` 文などの条件分岐で処理を制御し、`Font` プロパティなどで書式を変更する。これがVBAで「自動化」を実現する基本的な流れです。

今回ご紹介したコードは、あくまで「差分検出の入り口」であり、より高度な比較にはさらなる工夫が必要ですが、このコードをベースに、

  • 「大文字・小文字を区別しない比較」
  • 「特定の単語だけを比較対象にする」
  • 「変更箇所をコメントとして挿入する」

など、あなたの目的に合わせた機能を追加していくことができます。

マクロの記録で得られるのは「表面的な操作」ですが、今回のようにVBAコードを自分で書き、その仕組みを理解することで、Word VBAの「本質」に触れることができます。そうすれば、これまで「できない」と思っていたことが、「こうすればできる!」というアイデアに変わってくるはずです。

ぜひ、今回学んだことを活かして、あなただけの便利なWord VBAツールを作成してみてください。あなたのWord作業が、もっと快適で効率的になることを願っています!

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