使用Python解压部分.gz文件
这里有个问题。我有一个名为sample.gz的文件,大小大约是60KB。我想解压这个文件的前2000个字节。但是我遇到了CRC检查失败的错误。我猜这是因为gzip的CRC字段在文件的末尾,它需要整个压缩文件才能解压。有没有办法绕过这个问题?我不在乎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的头部信息?
4 个回答
我想不出有什么理由你会想要解压前2000个压缩字节。根据数据的不同,解压后可能会变成任意数量的输出字节。
你肯定是想解压整个文件,然后在解压到你需要的部分时停止,像这样:
f = gzip.GzipFile(fileobj=open('postcode-code.tar.gz', 'rb'))
data = f.read(4000)
print data
据我所知,这样做不会导致整个文件被读取。它只会读取解压前4000个字节所需的部分。
gzip模块的问题并不是它不能解压部分文件,而是在解压的最后阶段,它会尝试验证解压内容的校验和,这时就会出错。因为原始的校验和是存储在压缩文件的末尾,所以对于部分文件来说,验证永远都不会成功。
关键在于让gzip跳过这个验证。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()
看起来你需要了解一下 Python的zlib库。
GZIP格式是基于zlib的,但它增加了文件级别的压缩概念和CRC校验,这似乎是你现在不需要的。
比如可以看看这些 Doug Hellman的代码示例。
编辑:Doug Hellman网站上的代码只展示了如何使用zlib进行压缩或解压。正如上面提到的,GZIP就是“带封装的zlib”,你需要先解开这个封装,才能得到zlib压缩的数据 本身。这里有更多的信息,实际上并不复杂:
- 可以查看 RFC 1952,了解GZIP格式的详细信息。
- 这个格式以10个字节的头部开始,接着是一些可选的、未压缩的元素,比如文件名或注释,然后是zlib压缩的数据,最后是一个CRC-32(确切来说是“Adler32” CRC)。
- 使用 Python的struct模块,解析头部应该相对简单。
- 然后可以用Python的zlib模块解压这个zlib序列(或者它的前几千个字节,因为这正是你想要做的),就像上面的示例所展示的那样。
- 需要处理的可能问题:如果GZip归档中有多个文件,并且第二个文件在我们想要解压的几千字节的块内开始。
抱歉没有提供一个简单的步骤或现成的代码片段,不过按照上面的提示解码文件应该相对快速和简单。