在 Python 中使用 gzip 或 zlib 压缩字符串 - 为什么我缺少 "H4sIAAAAAAAA/" 位

Deflate string with gzip or zlib in Python - why am I missing the "H4sIAAAAAAAA/" bit

我正在尝试压缩 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=='

结果和我要的差不多,只是header部分不一样。两个结果(我的一个和预期的一个)都解压缩到同一个字符串,所以它们似乎都有效。我仍然想知道为什么我在 base64 压缩字符串中没有得到正确的 header。

我可以使用 zlib 获得更好的结果吗?我试过了,但得到了完全不同的结果,解压后也有效。

您有完全相同的压缩数据流。唯一的区别是您预期的数据流具有 MTIME field of the header set to 0 and the XFL flag set to 0, not 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() class 进行实际压缩,只要保留 mtime 参数,它就会对 MTIME 字段使用 time.time()默认 None.

我没想到这实际上 很重要 ,两个字符串将产生完全相同的解压缩数据。

如果你必须有相同的输出,那么最简单的方法就是替换header:

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:])

上面的内容将现有的 header 替换为将重要部分设置为与预期输出相同的值的内容; MTIME 和 XFL 标志都设置为 0。请注意,当您使用 gzip.compress() 时,只有 MTIME 字节会发生变化,并且在解压缩时实际上不会使用 XFL 字段。

虽然您可以使用gzip.GzipFile()class生成压缩输出并将 MTIME 设置为 0(传入 mtime=0),但您无法更改 XFL 字段的设置;当前 hard-coded to 2.

请注意,即使考虑到 MTIME 和 XFL 的差异,例如使用 DEFLATE compression algorithm 的不同实现压缩的数据仍然可能导致不同的压缩流,即使使用相同的压缩设置!这是因为 DEFLATE 根据片段的频率对数据进行编码,当压缩时有多个具有相同频率的片段可用时,不同的实现可以自由做出不同的选择。因此,测试数据是否已正确压缩的唯一正确方法是再次解压缩并比较结果。