Удалите строку с помощью gzip или zlib в Python - почему я пропускаю бит "H4sIAAAAAAAA/"
Я пытаюсь сжать строку в 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 ответ
У вас точно такой же сжатый поток данных. Единственное отличие состоит в том, что в ожидаемом потоке данных поле 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 кодирует данные на основе частоты фрагментов, и различные реализации могут свободно выбирать разные варианты при наличии нескольких фрагментов с одинаковой частотой, доступных при сжатии. Таким образом, единственный правильный способ проверить, правильно ли сжаты ваши данные, - это снова распаковать и сравнить результат.