【问题标题】:Unzipping part of a .gz file using python使用 python 解压缩 .gz 文件的一部分
【发布时间】:2010-12-16 12:07:04
【问题描述】:

所以这就是问题所在。我有大约 60KB 大小的 sample.gz 文件。我想解压这个文件的前 2000 个字节。我遇到了 CRC check failed 错误,我猜是因为 gzip CRC 字段出现在文件末尾,它需要整个 gzip 文件解压缩。有没有办法解决这个问题?我不关心CRC检查。即使我因为 CRC 错误而无法解压,也没关系。有没有办法解决这个问题并解压缩部分 .gz 文件?

我目前的代码是

import gzip
import time
import StringIO

file = open('sample.gz', 'rb')
mybuf = MyBuffer(file)
mybuf = StringIO.StringIO(file.read(2000))
f = gzip.GzipFile(fileobj=mybuf)
data = f.read()
print data

遇到的错误是

File "gunzip.py", line 27, in ?
    data = f.read()
File "/usr/local/lib/python2.4/gzip.py", line 218, in read
  self._read(readsize)
File "/usr/local/lib/python2.4/gzip.py", line 273, in _read
  self._read_eof()
File "/usr/local/lib/python2.4/gzip.py", line 309, in _read_eof
  raise IOError, "CRC check failed"
IOError: CRC check failed

还有什么方法可以使用 zlib 模块来做到这一点并忽略 gzip 标头?

【问题讨论】:

  • 因为我对前 4k 的压缩数据感兴趣。

标签: python gzip zlib


【解决方案1】:

gzip 模块的问题不是它不能解压部分文件,而是在它尝试验证解压内容的校验和时才出现错误。 (原始校验和存储在压缩文件的末尾,因此验证永远不会使用部分文件。)

关键是要欺骗 gzip 跳过验证。 answer by caesar0301 通过修改 gzip 源代码来做到这一点,但没有必要走那么远,简单的猴子补丁就可以了。我写了这个上下文管理器来临时替换gzip.GzipFile._read_eof,同时我解压部分文件:

import contextlib

@contextlib.contextmanager
def patch_gzip_for_partial():
    """
    Context manager that replaces gzip.GzipFile._read_eof with a no-op.

    This is useful when decompressing partial files, something that won't
    work if GzipFile does it's checksum comparison.

    """
    _read_eof = gzip.GzipFile._read_eof
    gzip.GzipFile._read_eof = lambda *args, **kwargs: None
    yield
    gzip.GzipFile._read_eof = _read_eof

示例用法:

from cStringIO import StringIO

with patch_gzip_for_partial():
    decompressed = gzip.GzipFile(StringIO(compressed)).read()

【讨论】:

    【解决方案2】:

    我似乎需要查看 Python zlib

    GZIP 格式依赖于 zlib,但引入了文件级压缩概念以及 CRC 检查,这似乎是您目前不想要/不需要的。

    例如这些code snippets from Dough Hellman

    编辑:Doubh Hellman 网站上的代码仅显示如何使用 zlib 进行压缩或解压缩。如上所述,GZIP 是“带有信封的 zlib”,您需要在获取 zlib 压缩数据之前对信封进行解码本身。这里有更多关于它的信息,它真的没有那么复杂:

    • 有关 GZIP 格式的详细信息,请参阅 RFC 1952
    • 此格式以 10 字节标头开头,后跟可选的非压缩元素,例如文件名或注释,然后是 zlib 压缩数据,其本身后跟 CRC-32(准确地说是“Adler32”CRC )。
    • 使用Python's struct module,解析头部应该比较简单
    • 然后可以使用 python 的 zlib 模块解压缩 zlib 序列(或它的前几千字节,因为这是您想要做的),如上面的示例所示
    • 可能需要处理的问题:如果 GZip 存档中有多个文件,并且如果第二个文件在我们希望解压缩的几千字节块内开始。

    很抱歉既没有提供简单的程序也没有提供现成的sn-p,但是使用上述指示解码文件应该相对快速和简单。

    【讨论】:

    • @mjv... 哪个特定代码 sn-p 适用于上面的示例。我浏览了链接并阅读了使用流。它没有任何地方声明它使用 gzip 流。我认为这适用于 zlib 流(已使用 zlib 流进行测试)
    • @unknown:检查我的编辑;代码 sn-ps 与纯 zlib 的压缩/解压缩有关。 GZip 格式意味着首先解析一个小的、未压缩的标头,然后再找到它的 zlip “有效负载”,如图所示。
    【解决方案3】:

    我看不出您想要解压缩前 2000 个压缩字节的任何可能原因。根据数据,这可能会解压缩为任意数量的输出字节。

    您当然要解压缩文件,并在您解压缩所需的文件时停止,例如:

    f = gzip.GzipFile(fileobj=open('postcode-code.tar.gz', 'rb'))
    data = f.read(4000)
    print data
    

    AFAIK,这不会导致读取整个文件。它只会读取前 4000 个字节所需的内容。

    【讨论】:

    • f.read(2000) 这里会读取前2000字节的解压数据。我对压缩数据的前 2000 字节感兴趣。
    • 为什么?你的应用到底是什么?
    • :-) 我试图在前 4k 数据中查找字符串“xyz”。假设我解压缩了 2K 的 gzip 数据并使用 4K 的解压缩数据着陆,我可以在这个 4k 中搜索/grep 字符串。所有的搜索代码都已经到位了..
    • 如果要搜索前 4k 的未压缩数据,请搜索前 4k 的未压缩数据,就像我在回答中所做的那样(可能将 4000 更改为 4096)。不要试图猜测 2k 会解压到 4k。它可能不会。它可能只解压到 2k,也可能解压到几兆字节。
    • 这是完美的。太感谢了!不需要肮脏的黑客攻击。
    【解决方案4】:

    我在Linux下使用我的python脚本读取gzip工具生成的压缩文件时也遇到了这个问题,原始文件丢失了。

    通过阅读Python的gzip.py的实现,我发现gzip.GzipFile有类似File类的方法,并利用python zip模块处理数据解压缩。同时,_read_eof()方法也存在,用于检查每个文件的CRC。

    但在某些情况下,例如处理没有正确 CRC 的 Stream 或 .gz 文件(我的问题),_read_eof() 将引发 IOError("CRC check failed")。因此,我尝试修改gzip模块以禁用CRC校验,最后这个问题消失了。

    def _read_eof(self):
        pass
    

    https://github.com/caesar0301/PcapEx/blob/master/live-scripts/gzip_mod.py

    我知道这是一个蛮力解决方案,但是使用 zip 模块重写一些低级方法可以节省很多时间,例如从压缩文件中逐个读取数据并逐行提取数据,大部分它已经存在于 gzip 模块中。

    贾敏

    【讨论】: