Получи случайную криптовалюту за регистрацию!

На самом деле архивы TAR оказались менее удобными в нашей теме | Python Заметки

На самом деле архивы TAR оказались менее удобными в нашей теме проверки идентичности. Давайте сделаем всё тоже самое для ZIP.

Допустим, тестовые файлы мы уже создали используя код из прошлого поста.
Теперь создадим архивы.

import zipfile

def create_zip(archive_path, files):
with zipfile.ZipFile(archive_path, "w") as zf:
for file in files:
zf.write(file)

create_zip('archive1.zip', files_to_archive)
create_zip('archive2.zip', files_to_archive)

Проверим хеш
>>> hashlib.md5(open("archive1.zip", "rb").read()).hexdigest()
'd54670be5e01e483797ee4ae30089423'
>>> hashlib.md5(open("archive2.zip", "rb").read()).hexdigest()
'd54670be5e01e483797ee4ae30089423'

Отлично! ZIP создаёт одинаковые архивы и сразу выдаёт одинаковую хеш-сумму!
Ну всё, на этом расходимся...


Хотя подождите ка, часто ли вы проверяете один и тот же файл на идентичность?
Давайте имитируем ситуацию когда файл был перезаписан или "модифицирован" но при этом фактически не изменился. То есть изменились только его атрибуты. Для этого можно использовать Linux-команду touch, которая обновляет время последнего доступа к файлу.

touch example_file0.txt
touch example_file1.txt
...

Либо альтернативу на Python

from pathlib import Path
for f in files_to_archive:
Path(f).touch()

Содержимое файлов не изменилось! Но изменились атрибуты. Пересоздаём второй архив.

create_zip('archive2.zip', files_to_archive)

Проверяем

>>> hashlib.md5(open("archive1.zip", "rb").read()).hexdigest()
'd54670be5e01e483797ee4ae30089423'
>>> hashlib.md5(open("archive2.zip", "rb").read()).hexdigest()
'aa508dbba4e223abe45e16dba4ad6e1f'

Вот это более правдивая ситуация.

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

def get_hash_zip(path):
hash_md5 = hashlib.md5()
with zipfile.ZipFile(path, "r") as z:
for f_name in z.namelist():
with z.open(f_name) as f:
hash_md5.update(f.read())
return hash_md5.hexdigest()

Сравним теперь хеш-суммы архивов

>>> get_hash_zip('archive1.zip')
'0b27c443737b0a84381b827e1d9a913b'
>>> get_hash_zip('archive2.zip')
'0b27c443737b0a84381b827e1d9a913b'

Всё чётко сработало!
А что по времени?

>>> timeit.timeit("get_hash_zip('archive1.zip')", number=100, globals=globals())
10.8

Ну тоже неплохо.

А теперь самая главная фишка ZIP - при создании архива он СРАЗУ записывает контрольную сумму файла в заголовки!
А это значит что мы можем просто считать готовые хеш-суммы и сравнить их! Это называется CRC (cyclic redundancy check)

def get_hash_zip2(path):
h = hashlib.md5()
for info in zipfile.ZipFile(path).infolist():
h.update(info.CRC.to_bytes(8, byteorder='big'))
return h.hexdigest()

>>> timeit.timeit("get_hash_zip2('archive1.zip')", number=100, globals=globals())
0.008

То есть даже буфер никакой не считывается, только несколько байт из заголовков каждого файла. Выполняется моментально. В моем случае 100 итераций за 8 мс!

Полный листинг тестов в Jupyter (для экспериментов жмём Open in Colab)
И просто в Gists

#libs #tricks