当比较两个 Word 文件时,如果唯一的差异是列表的编号方式,例如原本的 1. 和 2. 现在变成了 a. 和 b.,或者编号列表变成了项目符号列表,Aspose.Words 默认不会报告任何差异。本教程展示了在 Python 中比较 Word 文件时如何检测编号变化,并说明如何审阅、接受或拒绝检测到的更改。

关键要点

  • 默认情况下,当您比较 Word 文件时,列表的编号样式或项目符号字符的更改不会产生修订。
  • 设置 options.advanced_options.compare_list_definitions = True 会将每个受影响的列表段落报告为 FORMAT_CHANGE 修订。
  • 列表项内部的文本更改无论如何都会被报告。此选项仅在此基础上添加编号和项目符号的更改。
  • ignore_formatting=True 即使在启用该选项时也会隐藏编号更改。

为什么默认情况下编号更改不显示

Word 中的编号和项目符号来自列表定义。它描述了每个列表级别的外观:编号样式(1.、a.、i.)、项目符号字符以及相关格式。比较文档时,Aspose.Words 默认遵循 Microsoft Word,并在比较中省略列表定义。因此,如果仅更改了编号,比较将没有任何内容可报告。

compare_list_definitions 选项在 Word 中没有对应项。它告诉 Aspose.Words 在比较时包含列表定义。以下是我们在版本 26.9 中比较两项列表时看到的内容:

原始列表编辑后列表关闭选项开启选项
编号 1. 2.项目符号0 次修订2 FORMAT_CHANGE
编号 1. 2.字母编号 a. b.0 次修订2 FORMAT_CHANGE
编号, “项目 2”项目符号, “项目 二”2 (DELETION, INSERTION)4 (添加 2 FORMAT_CHANGE)
编号相同的编号列表0 次修订0 次修订

在实际操作中,最后一行很重要。启用此选项不会为两个文件中相同的列表创建额外的修订。

Prerequisites

您需要 Aspose.Words for Python via .NET 26.9 或更高版本。请从 PyPI 安装或升级它:

pip install --upgrade "aspose-words>=26.9"

没有许可证时,Aspose.Words 以受限的评估模式运行。一个临时许可证可以在您测试时移除这些限制。

查看差异:启用和未启用选项时的编号变化

以下脚本生成两个包含相同两项的文档。第一个使用编号列表,第二个使用项目符号列表。它会对它们进行两次比较,一次在关闭选项时,另一次在打开选项时,这样您就可以准确看到该选项的变化。

import datetime
import aspose.words as aw

def build_list_document(bulleted: bool) -> aw.Document:
    """Create a two-item list, either numbered or bulleted."""
    doc = aw.Document()
    builder = aw.DocumentBuilder(doc=doc)
    if bulleted:
        builder.list_format.apply_bullet_default()
    else:
        builder.list_format.apply_number_default()
    builder.writeln("Item 1")
    builder.writeln("Item 2")
    builder.list_format.remove_numbers()
    return doc

def compare_lists(compare_list_definitions: bool) -> aw.Document:
    original = build_list_document(bulleted=False)
    edited = build_list_document(bulleted=True)

options = aw.comparing.CompareOptions()
    options.advanced_options.compare_list_definitions = compare_list_definitions

# compare() writes revisions into `original`; it returns nothing.
    original.compare(
        document=edited,
        author="Reviewer",
        date_time=datetime.datetime.now(),
        options=options,
    )
    return original

for flag in (False, True):
    result = compare_lists(flag)
    print(f"compare_list_definitions={flag}: {result.revisions.count} revision(s)")
    for revision in result.revisions:
        revision_type = aw.RevisionType(revision.revision_type).name
        text = revision.parent_node.get_text().strip()
        print(f"  {revision_type}: {text}")

result.save("compared-lists.docx")

运行脚本会打印:

compare_list_definitions=False: 0 revision(s)
compare_list_definitions=True: 2 revision(s)
  FORMAT_CHANGE: Item 1
  FORMAT_CHANGE: Item 2

代码工作原理

  • build_list_document 使用 DocumentBuilder.list_format 开始一个默认的编号或项目符号列表,写入两个项目,然后调用 remove_numbers(),以确保后续段落不属于该列表。
  • CompareOptions.advanced_options 暴露 compare_list_definitions,其默认值为 False。
  • original.compare(...) 将 original 与 edited 进行比较,并将差异记录为 original 内的修订。该方法返回 None,因此始终从调用该方法的文档中读取结果。
  • 每个修订的 parent_node 是格式发生更改的列表段落。revision_type 为 RevisionType.FORMAT_CHANGE,用于编号和项目符号的更改。

检测两个 Word 文件之间的编号更改

在实际工作流中,您会比较两个现有文件,例如合同的两个版本。这些比较通常还包含其他修订,因此下面的脚本仅保留位于列表段落上的格式更改。

import datetime
import aspose.words as aw

original = aw.Document("contract-v1.docx")
edited = aw.Document("contract-v2.docx")

options = aw.comparing.CompareOptions()
options.advanced_options.compare_list_definitions = True

original.compare(edited, "Reviewer", datetime.datetime.now(), options)

# Keep only formatting revisions that sit on list paragraphs.
list_changes = [
    rev for rev in original.revisions
    if rev.revision_type == aw.RevisionType.FORMAT_CHANGE
    and rev.parent_node is not None
    and rev.parent_node.node_type == aw.NodeType.PARAGRAPH
    and rev.parent_node.as_paragraph().list_format.is_list_item
]

print(f"Total revisions: {original.revisions.count}")
print(f"Formatting changes on list paragraphs: {len(list_changes)}")
for rev in list_changes:
    print(f"  {rev.parent_node.get_text().strip()}")

original.save("contract-compared.docx")

在我们的测试中,合同的第 2 版将编号列表改为项目符号,并且将一个不相关的句子加粗。输出是:

Total revisions: 5
Formatting changes on list paragraphs: 2
  Invoices are due in 30 days.
  Late fees apply after 45 days.

过滤器会挑选出这两个列表项并跳过粗体更改,因为该修订位于文本上,而不是列表段落上。请记住,它会匹配列表项上的任何段落级别的格式更改,例如缩进的变化,而不仅仅是编号。如果需要区分这些更改,请检查已保存的文档。

审核、接受或拒绝编号更改

因为编号更改是普通的格式修订,您应像处理其他修订一样处理它们:

  • revisions.accept_all() 应用编辑后的列表格式。在第一个示例中,两个项目都变成了项目符号。
  • revisions.reject_all() 保持原始格式,因此项目仍然保持编号 1. 和 2.。
  • 保存文档会将修订写入为修订痕迹,这样审阅者可以在 Microsoft Word 中打开文件并自行决定每个更改。

您也可以对单个 Revision 对象调用 accept() 或 reject(),例如仅对前一节中的 list_changes 列表中的项目进行操作。

何时检测编号更改

当列表的编号对审阅者具有意义时,请打开此选项。法律和政策文档是常见的情况:将条款编号从 1. 更改为 (a) 会影响其他文档对它们的引用,因此审阅者需要看到此更改。当您在系统之间迁移文档并希望确认列表编号在迁移后保持不变时,这也很有用。

当您只关心文字内容时,请关闭它。内容审阅、翻译检查以及文本级别的差异在没有格式修订混入的情况下更容易阅读。

Things to Watch For

  • ignore_formatting 覆盖该选项。 如果将 options.ignore_formatting = True 设置为 True,则即使 compare_list_definitions = True,编号更改也不会被报告。
  • 在比较之前,文件不得包含修订。 如果任一文档已经有跟踪的更改,compare 会抛出 RuntimeError,提示比较的文档不得包含修订。请先接受或拒绝现有的修订。这也意味着您不能对已经包含第一次比较结果的文档进行第二次比较;每次都需要重新加载文件。
  • 其他修订仍会出现。 列表项中的插入或删除文本会被报告,无论该选项是开启还是关闭,因此如果需要将编号更改与文字更改区分开来,请检查 revision_type。

下一步

Aspose.Words for Python 文档中的 比较文档 文章介绍了其他比较设置,包括粒度、比较目标和忽略选项。

FAQs

  1. 为什么 Aspose.Words 在比较 Word 文件时未检测到编号更改? 默认情况下,Aspose.Words 遵循 Microsoft Word 的行为,将列表定义(例如编号样式或项目符号字符)排除在比较之外。如果列表从数字更改为项目符号,则不会产生修订,除非启用 compare_list_definitions。

  2. 如何在 Python 中检测编号更改? 创建 CompareOptions,将 options.advanced_options.compare_list_definitions 设置为 True,并将该选项传递给 Document.compare。每个编号或项目符号发生更改的列表段落都会收到 FORMAT_CHANGE 修订。

  3. 编号更改会创建哪种类型的修订?
    每个受影响的列表段落都会获得类型为 RevisionType.FORMAT_CHANGE 的修订。接受它会应用已编辑文件的列表格式,拒绝它则保留原始内容。

  4. 该选项能与 ignore_formatting 一起使用吗? 不。当 CompareOptions.ignore_formatting 为 True 时,即使 compare_list_definitions 为 True,也不会报告编号更改。

  5. Document.compare 返回什么? 没有返回值。它将差异作为修订写入调用它的文档中,因此您需要从该文档的 revisions 集合中读取它们。

  6. 哪个版本的 Aspose.Words for Python 添加了此选项? compare_list_definitions 属性(属于 AdvancedCompareOptions)是在 Aspose.Words for Python via .NET 26.9 中添加的。

获取免费许可证和支持