coru

Таблицы и данные на практике

Как сравнить два файла в Python по байтам, а не по имени и дате

Авторский пример разных файлов с одинаковыми именами, размерами и датами и небольшая локальная проверка на Python: совпадение, различие или ошибка.

С чего начать

Две копии с именем invoice.txt могут иметь одинаковый размер и дату изменения, но содержать разные числа. Перед тем как считать их взаимозаменяемыми, сравните байты. Небольшая программа читает два локальных файла и выдаёт IDENTICAL, DIFFERENT или ERROR; сами файлы она не перезаписывает и не удаляет.

Материал рассчитан на тех, кто уже запускает Python из терминала. Пример проверен в Linux с Python 3.12.3. Запуск в Windows и macOS для этой статьи не проверялся.

Разница, которой не видно в списке файлов

В нашем авторском опыте файлы находились в разных папках:

original/invoice.txt

text

amount=100

11 байт, включая завершающий перевод строки LF; у обоих файлов одинаковое время изменения.

copy/invoice.txt

text

amount=900

11 байт, включая завершающий перевод строки LF; у обоих файлов одинаковое время изменения.

Почему совпадения свойств недостаточно

Мы намеренно установили одинаковое время изменения. Это контролируемый контрпример, а не данные о частоте повреждения копий.

Функция Python filecmp.cmp() по умолчанию допускает сокращённую проверку по типу файла, размеру и времени изменения. На этих двух файлах обычный вызов вернул True, а вызов с shallow=False — False. Такое поведение описано в документации и не является ошибкой Python.

Прочитайте содержимое

Сохраните код ниже в рабочую папку как compare_files.py. Он читает двоичные данные порциями: другая кодировка текста или другой перевод строки не будут незаметно приведены к одному виду.

compare_files.py

python

import argparse
from pathlib import Path
import sys


def equal_bytes(left, right):
    for path in (left, right):
        if not Path(path).is_file():
            raise OSError(f"Not a readable regular file: {path}")
    with open(left, "rb") as a, open(right, "rb") as b:
        while True:
            left_chunk = a.read(1024 * 1024)
            right_chunk = b.read(1024 * 1024)
            if left_chunk != right_chunk:
                return False
            if not left_chunk:
                return True


def main():
    parser = argparse.ArgumentParser(description="Compare two local files by bytes.")
    parser.add_argument("left")
    parser.add_argument("right")
    args = parser.parse_args()
    try:
        same = equal_bytes(args.left, args.right)
    except (OSError, ValueError) as error:
        print(f"ERROR: {error}", file=sys.stderr)
        return 2
    print("IDENTICAL" if same else "DIFFERENT")
    return 0 if same else 1


if __name__ == "__main__":
    sys.exit(main())

Подготовьте два файла

Закройте приложения, способные изменить файлы, и дождитесь окончания копирования или синхронизации. Если облачный клиент показывает только значки ещё не загруженных файлов, сначала сделайте копии доступными локально. Затем укажите программе именно те два файла, которые нужно сравнить:

Запуск сравнения

sh

python3 compare_files.py "original/invoice.txt" "copy/invoice.txt"

Проверьте результат запуска

Подставьте свои пути; сохраните кавычки, если в пути есть пробелы. Для двух счетов из опыта программа выдаёт DIFFERENT. После записи точной последовательности байтов оригинала во второй тестовый файл она выдаёт IDENTICAL, даже если время изменения файлов различается.

Как понимать результат

  • IDENTICAL: прочитанные при этой проверке последовательности байтов совпали. Код завершения — 0.
  • DIFFERENT: последовательности различаются. Код завершения — 1. Сохраните оба файла, пока не выясните причину: результат не говорит, какая версия правильная.
  • ERROR: нужный файл не удалось проверить. Код завершения — 2. Исправьте путь или доступ и повторите запуск; этот результат ничего не сообщает о равенстве файлов.

Совпадение байтов не доказывает исправность файла

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

Когда различие ожидаемо

В нашем опыте переводы строк LF и CRLF считались разными; добавление метки порядка байтов UTF-8 также меняло результат. Такие файлы могут показывать похожий текст, оставаясь разными по байтам. Если вопрос звучит «в этих PDF написано одно и то же?» или «в CSV остались те же записи?», отдельно сравните документы или таблицы подходящим способом. Не удаляйте файл автоматически только из-за различия байтов.

Что именно проверено

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

Символическая ссылка ведёт к своему целевому файлу. Если дважды указать один и тот же файл, можно также получить IDENTICAL. Ни один из этих случаев не доказывает наличие независимой второй копии; различайте два нужных места хранения.

В локальном опыте проверены 14 случаев: в том числе изменение байта после двух порций по 1 048 576 байт, двоичные данные, пробелы и кириллица в именах, пустые файлы, отсутствующий путь, папка и именованный канал. Во всех завершённых проверках программа сохранила байты, размеры и время изменения исходных файлов. Опыт выполнен с изолированной сетью, где был доступен только интерфейс loopback. Это техническая проверка искусственных примеров, а не испытание всех файловых систем или гарантия качества резервной копии.

Источники и материалы

Помогите улучшить руководство

← Все заметки