ドキュメントの比較

ドキュメントの比較は、2 つのドキュメント間の変更を特定し、その変更をリビジョンとして含めるプロセスです。このプロセスでは、特定の 1 つのドキュメントのバージョンを含む 2 つのドキュメントを比較し、両方のドキュメント間の変更が最初のドキュメントのリビジョンとして表示されます。

比較方法は、文字レベルまたは単語レベルで単語を比較することによって実現されます。単語に少なくとも 1 文字の変更が含まれている場合、結果では、その違いは文字ではなく単語全体の変更として表示されます。この比較プロセスは、法律業界や金融業界では通常の作業です。

ドキュメント間やドキュメントの異なるバージョン間の相違点を手動で検索する代わりに、Aspose.Words を使用してドキュメントを比較し、書式設定、ヘッダー/フッター、表などの内容の変更を取得できます。

この記事では、ドキュメントを比較する方法と、高度な比較プロパティを指定する方法について説明します。

制限事項とサポートされるファイル形式

ドキュメントの比較は非常に複雑な機能です。コンテンツの組み合わせには、すべての違いを認識するために分析する必要があるさまざまな部分があります。この複雑さの理由は、Aspose.Words が Microsoft Word 比較アルゴリズムと同じ比較結果を取得することを目指しているためです。

比較される 2 つのドキュメントの一般的な制限は、compare メソッドを呼び出す前にドキュメントにリビジョンが存在してはいけないということです。この制限は Microsoft Word に存在します。

2 つのドキュメントの比較

文書を比較すると、後者の文書と前者の差異が、前者の改訂として現れます。ドキュメントを変更すると、比較メソッドの実行後に各編集に独自のリビジョンが適用されます。

Aspose.Words では、Compare メソッドを使用してドキュメントの相違点を識別できます。これは Microsoft Word ドキュメントの比較機能に似ています。これにより、ドキュメントまたはドキュメントのバージョンをチェックして、フォントの変更、間隔の変更、単語や段落の追加などの書式変更を含む相違点や変更点を見つけることができます。

比較の結果、ドキュメントが等しいか等しくないかを判断できます。 「等しい」ドキュメントという用語は、比較方法では変更をリビジョンとして表すことができないことを意味します。これは、ドキュメントのテキストとテキストの書式設定が同じであることを意味します。ただし、ドキュメント間には他にも違いがある可能性があります。たとえば、Microsoft Word はスタイルの形式リビジョンのみをサポートし、スタイルの挿入/削除を表すことはできません。したがって、ドキュメントには異なるスタイルのセットを含めることができますが、Compare メソッドでは依然としてリビジョンが生成されません。

次のコード例は、2 つのドキュメントが等しいかどうかを確認する方法を示しています。

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET
Document docA = new Document(dataDir + "TestFile.doc");
Document docB = new Document(dataDir + "TestFile - Copy.doc");
// DocA now contains changes as revisions.
docA.Compare(docB, "user", DateTime.Now);
if (docA.Revisions.Count == 0)
Console.WriteLine("Documents are equal");
else
Console.WriteLine("Documents are not equal");

次のコード例は、Compare メソッドを 2 つのドキュメントに単純に適用する方法を示しています。

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET
// The source document doc1.
Document doc1 = new Document();
DocumentBuilder builder = new DocumentBuilder(doc1);
builder.Writeln("This is the original document.");
// The target document doc2.
Document doc2 = new Document();
builder = new DocumentBuilder(doc2);
builder.Writeln("This is the edited document.");
// If either document has a revision, an exception will be thrown.
if (doc1.Revisions.Count == 0 && doc2.Revisions.Count == 0)
doc1.Compare(doc2, "authorName", DateTime.Now);
// If doc1 and doc2 are different, doc1 now has some revisions after the comparison, which can now be viewed and processed.
Assert.AreEqual(2, doc1.Revisions.Count);
foreach (Revision r in doc1.Revisions)
{
Console.WriteLine($"Revision type: {r.RevisionType}, on a node of type \"{r.ParentNode.NodeType}\"");
Console.WriteLine($"\tChanged text: \"{r.ParentNode.GetText()}\"");
}
// All the revisions in doc1 are differences between doc1 and doc2, so accepting them on doc1 transforms doc1 into doc2.
doc1.Revisions.AcceptAll();
// doc1, when saved, now resembles doc2.
doc1.Save(dataDir + "Document.Compare.docx");
doc1 = new Document(dataDir + "Document.Compare.docx");
Assert.AreEqual(0, doc1.Revisions.Count);
Assert.AreEqual(doc2.GetText().Trim(), doc1.GetText().Trim());

高度な比較オプションの指定

CompareOptions クラスには、ドキュメントを比較するときに適用できるさまざまなプロパティが多数あります。

たとえば、Aspose.Words を使用すると、元のドキュメント内の特定の種類のオブジェクトに対する比較操作中に行われた変更を無視できます。 「true」に設定すると、IgnoreHeadersAndFootersIgnoreFormattingIgnoreComments などのオブジェクト タイプに適切なプロパティを選択できます。

さらに、Aspose.Words には、変更を文字ごとに追跡するか単語ごとに追跡するかを指定できる Granularity プロパティが用意されています。

もう 1 つの共通のプロパティは、比較の変更をどのドキュメントで表示するかを選択することです。たとえば、Microsoft Word の [ドキュメントの比較] ダイアログ ボックスには、[変更内容を表示する] オプションがあります。これは比較結果にも影響します。 Aspose.Words は、この目的範囲 Target プロパティを提供します。

次のコード例は、詳細な比較プロパティを設定する方法を示しています。

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET
// Create the original document.
Document docOriginal = new Document();
DocumentBuilder builder = new DocumentBuilder(docOriginal);
// Insert paragraph text with an endnote.
builder.Writeln("Hello world! This is the first paragraph.");
builder.InsertFootnote(FootnoteType.Endnote, "Original endnote text.");
// Insert a table.
builder.StartTable();
builder.InsertCell();
builder.Write("Original cell 1 text");
builder.InsertCell();
builder.Write("Original cell 2 text");
builder.EndTable();
// Insert a textbox.
Shape textBox = builder.InsertShape(ShapeType.TextBox, 150, 20);
builder.MoveTo(textBox.FirstParagraph);
builder.Write("Original textbox contents");
// Insert a DATE field.
builder.MoveTo(docOriginal.FirstSection.Body.AppendParagraph(""));
builder.InsertField(" DATE ");
// Insert a comment.
Comment newComment = new Comment(docOriginal, "John Doe", "J.D.", DateTime.Now);
newComment.SetText("Original comment.");
builder.CurrentParagraph.AppendChild(newComment);
// Insert a header.
builder.MoveToHeaderFooter(HeaderFooterType.HeaderPrimary);
builder.Writeln("Original header contents.");
// Create a clone of our document, which we will edit and later compare to the original.
Document docEdited = (Document)docOriginal.Clone(true);
Paragraph firstParagraph = docEdited.FirstSection.Body.FirstParagraph;
// Change the formatting of the first paragraph, change casing of original characters and add text.
firstParagraph.Runs[0].Text = "hello world! this is the first paragraph, after editing.";
firstParagraph.ParagraphFormat.Style = docEdited.Styles[StyleIdentifier.Heading1];
// Edit the footnote.
Footnote footnote = (Footnote)docEdited.GetChild(NodeType.Footnote, 0, true);
footnote.FirstParagraph.Runs[1].Text = "Edited endnote text.";
// Edit the table.
Table table = (Table)docEdited.GetChild(NodeType.Table, 0, true);
table.FirstRow.Cells[1].FirstParagraph.Runs[0].Text = "Edited Cell 2 contents";
// Edit the textbox.
textBox = (Shape)docEdited.GetChild(NodeType.Shape, 0, true);
textBox.FirstParagraph.Runs[0].Text = "Edited textbox contents";
// Edit the DATE field.
FieldDate fieldDate = (FieldDate)docEdited.Range.Fields[0];
fieldDate.UseLunarCalendar = true;
// Edit the comment.
Comment comment = (Comment)docEdited.GetChild(NodeType.Comment, 0, true);
comment.FirstParagraph.Runs[0].Text = "Edited comment.";
// Edit the header.
docEdited.FirstSection.HeadersFooters[HeaderFooterType.HeaderPrimary].FirstParagraph.Runs[0].Text = "Edited header contents.";
// Apply different comparing options.
CompareOptions compareOptions = new CompareOptions();
compareOptions.IgnoreFormatting = false;
compareOptions.IgnoreCaseChanges = false;
compareOptions.IgnoreComments = false;
compareOptions.IgnoreTables = false;
compareOptions.IgnoreFields = false;
compareOptions.IgnoreFootnotes = false;
compareOptions.IgnoreTextboxes = false;
compareOptions.IgnoreHeadersAndFooters = false;
compareOptions.Target = ComparisonTargetType.New;
// compare both documents.
docOriginal.Compare(docEdited, "John Doe", DateTime.Now, compareOptions);
docOriginal.Save(dataDir + "Document.CompareOptions.docx");
docOriginal = new Document(dataDir + "Document.CompareOptions.docx");
// If you set compareOptions to ignore certain types of changes,
// then revisions done on those types of nodes will not appear in the output document.
// You can tell what kind of node a revision was done on by looking at the NodeType of the revision's parent nodes.
Assert.AreNotEqual(compareOptions.IgnoreFormatting, docOriginal.Revisions.Any(rev => rev.RevisionType == RevisionType.FormatChange));
Assert.AreNotEqual(compareOptions.IgnoreCaseChanges, docOriginal.Revisions.Any(s => s.ParentNode.GetText().Contains("hello")));
Assert.AreNotEqual(compareOptions.IgnoreComments, docOriginal.Revisions.Any(rev => HasParentOfType(rev, NodeType.Comment)));
Assert.AreNotEqual(compareOptions.IgnoreTables, docOriginal.Revisions.Any(rev => HasParentOfType(rev, NodeType.Table)));
Assert.AreNotEqual(compareOptions.IgnoreFields, docOriginal.Revisions.Any(rev => HasParentOfType(rev, NodeType.FieldStart)));
Assert.AreNotEqual(compareOptions.IgnoreFootnotes, docOriginal.Revisions.Any(rev => HasParentOfType(rev, NodeType.Footnote)));
Assert.AreNotEqual(compareOptions.IgnoreTextboxes, docOriginal.Revisions.Any(rev => HasParentOfType(rev, NodeType.Shape)));
Assert.AreNotEqual(compareOptions.IgnoreHeadersAndFooters, docOriginal.Revisions.Any(rev => HasParentOfType(rev, NodeType.HeaderFooter)));