0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++ で 2 つの Word 文書を比較し差分を検出する実装手法

0
Posted at

C++ で開発したアプリケーションにおいて、2 つの Word 文書を比較し、その差分を検出したいという要件に直面することがあります。例えば、契約書の改訂履歴の確認、複数人で編集した文書の変更点把握、提出された課題の修正箇所チェックなどが典型的なユースケースです 。

文書比較を手動で行うのは時間がかかるうえ、見落としも発生しやすくなります。特に長文の文書や、頻繁に更新される文書では、プログラムによる自動化が有効です。本記事では、C++ から 2 つの Word 文書を比較し、差分を抽出する実装手法について解説します。

Word 文書比較の技術的課題

Word 文書をプログラムで比較する際には、以下のような技術的課題があります。

差分の検出レベル

文書比較では、テキストの変更だけでなく、書式設定(フォント、色、スタイルなど)、表の構造変更、画像の差し替え、ヘッダー・フッターの変更など、多岐にわたる要素を検出する必要があります。比較の粒度(文字単位か単語単位か)によっても結果が変わります 。

変更履歴(リビジョン)の扱い

比較対象の文書に未確定の変更履歴(Track Changes)が残っている場合、正確な比較ができません。比較処理の前に、すべてのリビジョンを確定(承諾)しておく必要があります 。

比較結果の出力形式

差分をどのように出力するかも重要な検討事項です。一般的には、変更箇所を赤線や取り消し線で表示した新しい Word 文書として出力する方法が取られます 。

実装アプローチの選択肢

C++ から Word 文書を比較する方法としては、以下のような選択肢があります。

手法 メリット デメリット
サードパーティライブラリ(例:Spire.Doc) Office 不要、組み込みが容易 ライセンスコストが発生する場合あり
クラウド API サービス 環境構築不要、高精度 インターネット接続必須、従量課金
COM オートメーション Office と同等の精度 Windows 限定、Office 要インストール

本記事では、外部依存が少なく C++ アプリケーションに直接組み込めるサードパーティライブラリを用いる手法について、Spire.Doc for C++ を具体例として実装方法を示します。

環境構築

入手方法

Spire.Doc for C++ は、以下のいずれかの方法でプロジェクトに導入できます 。

  1. NuGet パッケージマネージャー経由
    Visual Studio の「NuGet パッケージの管理」から Spire.Doc.Cpp を検索してインストール

  2. 手動での組み込み
    配布パッケージを入手し、インクルードディレクトリとライブラリファイルをプロジェクトに追加

評価目的であれば、一時ライセンスを申請することで機能制限なく試用できます。

必要なインクルード

#include "Spire.Doc.o.h"

using namespace Spire::Doc;

基本的な比較処理

2 つの Word 文書を比較する

以下は、2 つの Word 文書を比較し、差分を 3 つ目の文書として保存する基本的なコードです 。

#include "Spire.Doc.o.h"

using namespace Spire::Doc;

int main() {
    // 1 つ目の文書を読み込み
    intrusive_ptr<Document> doc1 = new Document();
    doc1->LoadFromFile(L"C:\\documents\\original.docx");

    // 2 つ目の文書を読み込み
    intrusive_ptr<Document> doc2 = new Document();
    doc2->LoadFromFile(L"C:\\documents\\revised.docx");

    // doc1 を基準として doc2 を比較
    // 第 2 引数は変更者名(差分に表示される)
    doc1->Compare(doc2, L"変更者");

    // 比較結果を保存
    doc1->SaveToFile(L"C:\\output\\comparison_result.docx", FileFormat::Docx2013);

    doc1->Close();
    doc2->Close();

    return 0;
}

上記のコードでは以下の処理を行っています。

  1. Document オブジェクトを 2 つ生成し、比較対象の文書を読み込み
  2. Compare メソッドで doc1 を基準として doc2 を比較(第 2 引数は変更者名)
  3. SaveToFile メソッドで比較結果を新しい文書として保存

生成された比較結果の文書を Microsoft Word で開くと、追加・削除・書式変更などの差分が視覚的に確認できます 。

応用的な比較オプション

比較オプションのカスタマイズ

特定の要素を比較対象から除外したい場合は、CompareOptions クラスを使用して比較の挙動をカスタマイズできます 。

#include "Spire.Doc.o.h"

using namespace Spire::Doc;
using namespace Spire::Doc::Documents::Comparison;

int main() {
    intrusive_ptr<Document> doc1 = new Document();
    doc1->LoadFromFile(L"C:\\documents\\original.docx");

    intrusive_ptr<Document> doc2 = new Document();
    doc2->LoadFromFile(L"C:\\documents\\revised.docx");

    // 比較オプションを設定
    intrusive_ptr<CompareOptions> options = new CompareOptions();
    
    // 書式の変更を無視する
    options->SetIgnoreFormatting(true);
    
    // ヘッダーとフッターの変更を無視する
    options->SetIgnoreHeadersAndFooters(true);

    // オプションを指定して比較
    doc1->Compare(doc2, L"変更者", options);

    doc1->SaveToFile(L"C:\\output\\comparison_result.docx", FileFormat::Docx2013);

    doc1->Close();
    doc2->Close();

    return 0;
}

主なオプションは以下の通りです 。

プロパティ 説明 デフォルト値
SetIgnoreFormatting 書式変更を無視するかどうか false
SetIgnoreHeadersAndFooters ヘッダー・フッターの変更を無視するかどうか false
SetTextCompareLevel テキスト比較の粒度(文字単位/単語単位) Word

複数の文書を順次比較する

大量の文書ペアを比較する場合は、関数化してループ処理を行うと効率的です。

#include "Spire.Doc.o.h"
#include <vector>
#include <string>

using namespace Spire::Doc;
using namespace std;

void CompareDocuments(const wstring& originalPath, 
                      const wstring& revisedPath, 
                      const wstring& outputPath,
                      const wstring& author) {
    intrusive_ptr<Document> doc1 = new Document();
    doc1->LoadFromFile(originalPath.c_str());

    intrusive_ptr<Document> doc2 = new Document();
    doc2->LoadFromFile(revisedPath.c_str());

    doc1->Compare(doc2, author.c_str());
    doc1->SaveToFile(outputPath.c_str(), FileFormat::Docx2013);

    doc1->Close();
    doc2->Close();
}

int main() {
    // 比較対象のファイルペアを定義
    vector<tuple<wstring, wstring, wstring>> tasks = {
        { L"doc1_orig.docx", L"doc1_rev.docx", L"doc1_result.docx" },
        { L"doc2_orig.docx", L"doc2_rev.docx", L"doc2_result.docx" },
    };

    for (const auto& task : tasks) {
        CompareDocuments(
            L"C:\\documents\\" + get<0>(task),
            L"C:\\documents\\" + get<1>(task),
            L"C:\\output\\" + get<2>(task),
            L"システム"
        );
    }

    return 0;
}

注意点と制限事項

動作環境

  • プラットフォーム: Windows、Linux に対応
  • アーキテクチャ: 64 ビットアプリケーション向け
  • Microsoft Office: 不要(ライブラリ単体で動作)

比較前の準備

比較対象の文書に未承諾の変更履歴(Track Changes)が含まれている場合、正しい比較結果が得られないことがあります。可能であれば、比較前に AcceptAllRevisions 相当の処理を行うことを推奨します 。

メモリ管理

大きな Word 文書を処理する場合、メモリ消費量が増加することがあります。複数文書を連続して処理する際は、各処理後に Close() を呼び出してリソースを解放することを推奨します。

評価版の制限

評価版では、生成される文書に評価版であることを示す透かしが挿入されます。本番環境で使用する場合はライセンスの適用が必要です。

おわりに

本記事では、C++ 環境において 2 つの Word 文書を比較し差分を検出する一手法として、Spire.Doc for C++ を用いた実装例を示した。基本的な比較処理は数行のコードで実現でき、書式変更の無視やヘッダー・フッターの除外といったオプション指定も可能である。

文書比較の自動化は、契約書レビューや品質管理、共同編集ワークフローなど、さまざまな業務シーンで活用できる 。実際の開発においては、プロジェクトの要件や運用環境に応じて適切な手法を選択されたい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?