如何使用包含额外数据的Gzip文件?

时间:2011-02-08 01:02:51

标签: python gzip

我正在编写一个脚本,它将使用来自仪器的数据作为gzip流。在大约90%的情况下,gzip模块完美地工作,但是一些流导致它生成IOError: Not a gzipped file。如果删除gzip标头并将deflate流直接送到zlib,我会获得Error -3 while decompressing data: incorrect header check。在将我的头撞到墙上大约半天之后,我发现有问题的流包含一个看似随机数量的额外字节(不是gzip数据的一部分)附加到最后。

令我感到奇怪的是Python无法使用这些文件有两个原因:

  1. Gzip和7zip都能够毫无问题地打开这些“填充”文件。 (Gzip生成消息decompression OK, trailing garbage ignored,7zip静默成功。)
  2. Gzip和Python文档似乎都表明这应该有效:(强调我的)

    Gzip's format.txt

      

    必须有可能   使用任何压缩方法检测压缩数据的结尾,   无论压缩数据的实际大小如何。 特别是,   解压缩器必须能够检测并跳过附加的额外数据   到面向记录的文件系统上的有效压缩文件,或者何时   压缩数据只能以a的倍数从设备读取   一定的块大小。

    Python's gzip.GzipFile`

      

    调用GzipFile对象的close()方法无法关闭 fileobj ,因为您可能希望在压缩数据后附加更多资料。这也允许您传递一个StringIO对象作为 fileobj 进行写入,并使用StringIO对象的getvalue()方法检索生成的内存缓冲区。

    Python's zlib.Decompress.unused_data

      

    包含压缩数据末尾之后的任何字节的字符串。也就是说,这将保持"",直到包含压缩数据的最后一个字节可用。 如果整个字符串结果包含压缩数据,则为"",空字符串。

         

    确定压缩数据字符串结束位置的唯一方法是实际解压缩。这意味着当压缩数据包含在较大文件的一部分时,您只能通过读取数据并将其后跟一些非空字符串放入解压缩对象的decompress() 方法,直到unused_data属性不再是空字符串。

  3. 以下是我尝试过的四种方法。 (这些例子是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:])
    

4 个答案:

答案 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()

https://docs.python.org/3/library/zlib.html

答案 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])

完美无缺