2011-02-08 333 views
12

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

这令我奇怪的是,Python不能使用这些文件的原因有两个:

  1. GZIP和7zip的能够打开这些“填充”的文件没有问题。 (Gzip已产生的消息decompression OK, trailing garbage ignored,7zip的成功默默)
  2. 无论是gzip和Python文档似乎表明,这应该工作:(重点煤矿)

    Gzip's format.txt

    必须能够到 无论压缩数据的实际大小如何,都可以使用任何压缩方法检测压缩数据的结尾,即 。 特别地, 解压缩器必须能够面向记录的文件系统上的检测和跳过所附 额外的数据为有效的压缩文件,或者当 的压缩数据只能从一个装置在一个 的倍数读一定的块大小。

    Python's gzip.GzipFile`

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

我做错了什么?

UPDATE

好,同时与gzip的问题似乎是在模块中的一个错误,我zlib问题是自己造成的。 ;-)

在挖掘到gzip.py时我意识到我在做什么错了 - 默认情况下,zlib.decompress等。期望zlib-wrapped流,而不是裸露的缩小流。通过传入负值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:]) 

回答

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标准库。

+0

其实我搞乱大约有`gzip`的内部位在调试的问题,但它没有发生,我来解决那里的问题并用我的脚本打包修改后的模块。这真是丑陋,但听起来它可能仍然是最好的选择。 : -/ – 2011-02-08 01:29:56

+0

@Ben:它是独立的,至少它不是一项重大成本;只有一个文件。本地模块更令人讨厌。 – 2011-02-08 01:34:11

4

我在过去类似的问题。我写了一个new module,与流更好地协作。你可以尝试一下,看看它是否适合你。

-1

我不能使它与上述技术工作。所以做了一个变通使用的压缩文件包

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

运行完美

相关问题