我正在编写一个脚本,它将以 gzip 流的形式处理来自仪器的数据。在大约 90% 的情况下,gzip
模块工作完美,但某些流导致它产生IOError: Not a gzipped file
。如果 gzip 标头被删除并且 deflate 流直接馈送到zlib
,我反而得到Error -3 while decompressing data: incorrect header check
。经过大约半天的头撞墙后,我发现有问题的流末尾附加了看似随机数量的额外字节(不是 gzip 数据的一部分)。
让我感到奇怪的是,Python 无法使用这些文件有两个原因:
- Gzip 和 7zip 都可以毫无问题地打开这些“填充”文件。 (Gzip 产生消息
decompression OK, trailing garbage ignored
,7zip 默默地成功了。)
-
Gzip 和 Python 文档似乎都表明这应该有效:(强调我的)
Gzip的格式.txt http://www.gzip.org/format.txt:
一定可以
使用任何压缩方法检测压缩数据的结尾,
无论压缩数据的实际大小如何。尤其,
解压缩器必须能够检测并跳过附加的额外数据
到有效的压缩文件在面向记录的文件系统上,或者当
压缩数据只能从设备中读取
一定的块大小。
Python 的 gzip.GzipFile` http://docs.python.org/library/gzip.html#gzip.GzipFile:
呼叫一个GzipFile
对象的close()
方法没有关闭fileobj, 因为您可能希望在压缩数据后附加更多材料。这也允许您通过StringIO
打开对象以写入为fileobj,并使用检索结果内存缓冲区StringIO
对象的getvalue()
method.
蟒蛇的zlib.Decompress.unused_data http://docs.python.org/library/zlib.html#zlib.Decompress.unused_data:
包含压缩数据末尾之后的任何字节的字符串。也就是说,这仍然是""
直到包含压缩数据的最后一个字节可用。如果整个字符串包含压缩数据,则这是""
,空字符串。
确定压缩数据字符串结束位置的唯一方法是实际解压缩它。这意味着当压缩数据包含在较大文件的一部分时,您只能通过以下方式找到它的结尾读取数据并将其后跟一些非空字符串输入解压对象的decompress()
方法直到unused_data
属性不再是空字符串。
这是我尝试过的四种方法。 (这些示例是 Python 3.1,但我测试了 2.5 和 2.7,也遇到了同样的问题。)
# approach 1 - gzip.open
with gzip.open(filename) as datafile:
data = datafile.read()
# approach 2 - gzip.GzipFile
with open(filename, "rb") as gzipfile:
with gzip.GzipFile(fileobj=gzipfile) as datafile:
data = datafile.read()
# approach 3 - zlib.decompress
with open(filename, "rb") as gzipfile:
data = zlib.decompress(gzipfile.read()[10:])
# approach 4 - zlib.decompressobj
with open(filename, "rb") as gzipfile:
decompressor = zlib.decompressobj()
data = decompressor.decompress(gzipfile.read()[10:])
难道我做错了什么?
UPDATE
好吧,虽然问题是gzip
似乎是模块中的错误,我的zlib
问题都是自己造成的。 ;-)
在深入挖掘的同时gzip.py
我意识到我做错了什么——默认情况下,zlib.decompress
等人。期望 zlib 包装的流,而不是裸露的 deflate 流。通过传递负值wbits
,你可以告诉zlib
跳过 zlib 标头并解压缩原始流。这两者都有效:
# approach 5 - zlib.decompress with negative wbits
with open(filename, "rb") as gzipfile:
data = zlib.decompress(gzipfile.read()[10:], -zlib.MAX_WBITS)
# approach 6 - zlib.decompressobj with negative wbits
with open(filename, "rb") as gzipfile:
decompressor = zlib.decompressobj(-zlib.MAX_WBITS)
data = decompressor.decompress(gzipfile.read()[10:])