Удалите строку с помощью gzip или zlib в Python - почему я пропускаю бит "H4sIAAAAAAAA /" - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь сжать строку в Python, но мой результат не соответствует ожиданиям.

Например, строку, которую я пытаюсь сжать:

<?xml version='1.0' encoding='UTF-8'?>

Вот чтомой конечный результат должен быть:

H4sIAAAAAAAA/7Oxr8jNUShLLSrOzM+zVTfUM1BXSM1Lzk/JzEu3VQ8NcdO1ULe3AwBHQvxaJgAAAA==

Первая попытка:

base64.b64encode(gzip.compress("<?xml version='1.0' encoding='UTF-8'?>".encode('utf-8')))

Результаты:

b'H4sIAHDj6lsC/7Oxr8jNUShLLSrOzM+zVTfUM1BXSM1Lzk/JzEu3VQ8NcdO1ULe3AwBHQvxaJgAAAA=='

Результат - почти то, что я ищу, ночасть заголовка отличается.Оба результата (мой и ожидаемый) распаковываются в одну и ту же строку, поэтому они оба работают.Я все еще хотел бы знать, почему я не получаю правильный заголовок в сжатой строке base64.

Могу ли я получить лучший результат, используя zlib?Я пытался, но получил совершенно другой результат, который работал и при распаковке.

1 Ответ

0 голосов
/ 13 ноября 2018

У вас точно такой же сжатый поток данных .Единственное отличие состоит в том, что ожидаемый поток данных имеет поле MTIME заголовка, установленное на 0, и флаг XFL, установленный на 0, а не 2 :

>>> from base64 import b64decode
>>> expected = b64decode('H4sIAAAAAAAA/7Oxr8jNUShLLSrOzM+zVTfUM1BXSM1Lzk/JzEu3VQ8NcdO1ULe3AwBHQvxaJgAAAA==')
>>> actual = b64decode('H4sIAHDj6lsC/7Oxr8jNUShLLSrOzM+zVTfUM1BXSM1Lzk/JzEu3VQ8NcdO1ULe3AwBHQvxaJgAAAA==')
>>> expected[:4] == actual[:4]  # identification, compression method and flag match
True
>>> expected[4:8], actual[4:8]  # mtime bytes differ, zero vs. current time
(b'\x00\x00\x00\x00', b'p\xe3\xea[')
>>> from datetime import datetime
>>> print(datetime.fromtimestamp(int.from_bytes(actual[4:8], 'little')))
2018-11-13 14:45:04
>>> expected[8], actual[8]  # XFL is set to 2 in the actual output
(0, 2)
>>> expected[9], actual[9]  # OS set to *unknown* in both
(255, 255)
>>> expected[10:] == actual[10:]  # compressed data payload is the same
True

Функция gzip.compress()просто использует gzip.GzipFile() класс для фактического сжатия, и он будет использовать time.time() для поля MTIME всякий раз, когда аргумент mtime оставлен по умолчанию None.

Я бы не ожидал, что на самом деле имеет значение , обе строки приведут к точно таким же распакованным данным.

Если вы должны иметь одинаковый вывод, тосамый простой способ - просто заменить заголовок :

compressed = gzip.compress("<?xml version='1.0' encoding='UTF-8'?>".encode('utf-8'))
result = base64.b64encode(b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff' + compressed[10:])

Приведенный выше текст заменяет существующий заголовок на тот, в котором для частей, которые имеют значение, установлены те же значения, что и ожидаемый результат;MTIME и флаг XFL установлены в 0. Обратите внимание, что при использовании gzip.compress() изменяются только байты MTIME, и поле XFL фактически не используется при распаковке.

Хотя вы могли бы используйте класс gzip.GzipFile() для получения сжатого вывода с MTIME, установленным в 0 (введите mtime=0), вы не можете изменить значение поля XFL;в настоящее время жестко закодировано в 2.

Обратите внимание, что даже с учетом различий MTIME и XFL, таких как данные, сжатые с помощью различных реализаций алгоритма сжатия DEFLATE может привести к другому сжатому потоку, даже при использовании тех же настроек сжатия!Это связано с тем, что DEFLATE кодирует данные на основе частоты фрагментов, и различные реализации могут свободно выбирать различные варианты при наличии нескольких фрагментов с одинаковой частотой, доступных при сжатии.Поэтому единственный правильный способ проверить, правильно ли сжаты ваши данные, - это снова распаковать и сравнить результат.

...