在 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 根据片段的频率对数据进行编码,当压缩时有多个具有相同频率的片段可用时,不同的实现可以自由做出不同的选择。因此,测试数据是否已正确压缩的唯一正确方法是再次解压缩并比较结果。
我正在尝试压缩 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 根据片段的频率对数据进行编码,当压缩时有多个具有相同频率的片段可用时,不同的实现可以自由做出不同的选择。因此,测试数据是否已正确压缩的唯一正确方法是再次解压缩并比较结果。