我正在编写一个脚本,它将使用来自仪器的数据作为gzip流。在大约90%的情况下,gzip
模块完美地工作,但是一些流导致它生成IOError: Not a gzipped file
。如果删除gzip标头并将deflate流直接送到zlib
,我会获得Error -3 while decompressing data: incorrect header check
。在将我的头撞到墙上大约半天之后,我发现有问题的流包含一个看似随机数量的额外字节(不是gzip数据的一部分)附加到最后。
令我感到奇怪的是Python无法使用这些文件有两个原因:
decompression OK, trailing garbage ignored
,7zip静默成功。)Gzip和Python文档似乎都表明这应该有效:(强调我的)
必须有可能 使用任何压缩方法检测压缩数据的结尾, 无论压缩数据的实际大小如何。 特别是, 解压缩器必须能够检测并跳过附加的额外数据 到面向记录的文件系统上的有效压缩文件,或者何时 压缩数据只能以a的倍数从设备读取 一定的块大小。
调用
GzipFile
对象的close()
方法无法关闭 fileobj ,,因为您可能希望在压缩数据后附加更多资料。这也允许您传递一个StringIO
对象作为 fileobj 进行写入,并使用StringIO
对象的getvalue()
方法检索生成的内存缓冲区。
Python's 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:])
我做错了吗?
更新
好的,虽然gzip
的问题似乎是模块中的错误,但我的zlib
问题是自我造成的。 ; - )
在深入研究gzip.py
时,我意识到我做错了什么 - 默认情况下,zlib.decompress
等。期待zlib包裹的流,而不是裸露的流。通过传递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:])
答案 0 :(得分:18)
这是一个错误。 Python中gzip模块的质量远远低于Python标准库中应该要求的质量。
这里的问题是gzip模块假定该文件是gzip格式文件的流。在压缩数据的末尾,它从头开始,期待一个新的gzip头;如果找不到,则引发异常。这是错误的。
当然, 有效连接两个gzip文件,例如:
echo testing > test.txt
gzip test.txt
cat test.txt.gz test.txt.gz > test2.txt.gz
zcat test2.txt.gz
# testing
# testing
gzip模块的错误是,如果第二次没有gzip头,它不应该引发异常;它应该只是结束文件。如果第一次没有标题,它应该仅引发异常。
没有直接修改gzip模块就没有干净的解决方法;如果你想这样做,请查看_read
方法的底部。它应该设置另一个标志,例如。 reading_second_block
,告诉_read_gzip_header
举起EOFError
代替IOError
。
此模块中还有其他错误。例如,它不必要地寻求,导致它在不可搜索的流(例如网络套接字)上失败。这让我对这个模块几乎没有信心:一个不知道gzip需要在没有搜索的情况下运行的开发人员在Python标准库中实现它是非常不合格的。
答案 1 :(得分:5)
过去我遇到过类似的问题。我写了一个new module,它对流更好。你可以尝试一下,看看它是否适合你。
答案 2 :(得分:1)
我确实有这个问题,但是这些答案都没有解决我的问题。因此,这是我为解决此问题所做的事情:
#for gzip files
unzipped = zlib.decompress(gzip_data, zlib.MAX_WBITS|16)
#for zlib files
unzipped = zlib.decompress(gzip_data, zlib.MAX_WBITS)
#automatic header detection (zlib or gzip):
unzipped = zlib.decompress(gzip_data, zlib.MAX_WBITS|32)
根据您的情况,可能需要对数据进行解码,例如:
unzipped = unzipped.decode()
答案 3 :(得分:-1)
我无法使用上述技术。所以做了一个使用zipfile包的工作
import zipfile
from io import BytesIO
mock_file = BytesIO(data) #data is the compressed string
z = zipfile.ZipFile(file = mock_file)
neat_data = z.read(z.namelist()[0])
完美无缺