Когда вы сравниваете два файла Word, и единственное различие заключается в нумерации списка, например пункты, которые были 1. и 2. теперь стали a. и b., или нумерованный список превратился в маркированный, Aspose.Words по умолчанию не сообщает о различиях. Этот учебник показывает, как обнаружить изменения нумерации при сравнении файлов Word в Python. Он также объясняет, как просматривать, принимать или отклонять найденные изменения.

Ключевые выводы

  • По умолчанию изменение стиля нумерации списка или символа маркера не приводит к появлению ревизий при сравнении файлов 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 изменений

Последняя строка имеет значение на практике. Включение этой опции не создает дополнительных ревизий для списков, которые одинаковы в обоих файлах.

Требования

Вам требуется 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 и решить по каждому изменению самостоятельно.

Вы также можете вызвать accept() или reject() для отдельных объектов Revision, например, только для элементов списка list_changes, указанных в предыдущем разделе.

Когда следует обнаруживать изменения нумерации

Включите эту опцию, когда нумерация списка имеет значение для ваших рецензентов. Юридические и нормативные документы — типичный пример: переименование пунктов с 1. на (a) меняет то, как другие документы ссылаются на них, поэтому рецензенту необходимо видеть это. Это также полезно, когда вы переносите документы между системами и хотите убедиться, что нумерация списка сохранилась без изменений.

Отключайте его, если вам важна только формулировка. Обзоры контента, проверки перевода и диффы на уровне текста легче читать без смешанных с ними правок форматирования.

На что обратить внимание

  • ignore_formatting переопределяет параметр. Если вы установите options.ignore_formatting = True, изменения нумерации не будут сообщаться, даже при compare_list_definitions = True.
  • Файлы не должны содержать ревизий перед сравнением. Если в любом из документов уже есть отслеживаемые изменения, compare генерирует RuntimeError, сообщающий, что сравниваемые документы не должны иметь ревизий. Сначала примите или отклоните существующие ревизии. Это также означает, что вы не можете выполнить второе сравнение документа, в котором уже находятся результаты первого; каждый раз перегружайте файлы.
  • Другие ревизии всё равно отображаются. Вставленный или удалённый текст в пункте списка фиксируется независимо от того, включён параметр или нет, поэтому проверяйте revision_type, если нужно отделить изменения нумерации от изменений формулировок.

Следующие шаги

Статья Compare Documents в документации Aspose.Words for Python охватывает остальные настройки сравнения, включая детализацию, цель сравнения и параметры игнорирования.

Часто задаваемые вопросы

  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.

Получите бесплатную лицензию и поддержку