从gzip文件中写入未压缩文件的内存高效方法

3

使用Python 3.5

我正在解压缩一个gzip文件,并将其写入另一个文件。在查找内存不足问题时,我在gzip模块的文档中找到了一个示例:

import gzip
import shutil
with open('/home/joe/file.txt', 'rb') as f_in:
    with gzip.open('/home/joe/file.txt.gz', 'wb') as f_out:
        shutil.copyfileobj(f_in, f_out)

这段代码实现了压缩功能,我需要解压缩,因此我认为只需反转模式即可,如下所示:
with open(unzipped_file, 'wb') as f_out, gzip.open(zipped_file, 'rb') as f_in:
    shutil.copyfileobj(f_in, f_out)

我的问题是,为什么我在以下代码中遇到了内存问题:
with gzip.open(zipped_file, 'rb') as zin, open(unzipped_file, 'wb') as wout:
    wout.write(zin.read())

我可能是在最后一根稻草上趴着,或者我天真地认为这些文件会像生成器一样流式解压过程,占用非常少的内存。这两种方法是否等效?


我建议您查看shutil.copyfileObj方法的代码。 - Vinit Kumar
“我太天真了,以为这些文件会像生成器一样运作。”-- 就是这样。为了证明,可以尝试对一些较小的文件使用print(type(zin.read())) - Robᵩ
copyfileobj 函数使用文件对象,并在内部执行类似于 file.read(blocksize)file.write(chunk) 的读写操作。从这个意义上说,使用 copyfileobj 可能并不比直接使用 readwrite 方法提供显著的优势。 - undefined
2个回答

3

这是 shutil.copyfileObj 方法。

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)

它按16*1024的长度将文件分块读取。当您尝试反转该过程时,没有考虑文件大小会被读入内存并导致内存问题。

我认为这是更加简洁的解决方案,而不是像我展示的那样基本上复制它。 - mohawkTrail
我不喜欢使用break语句来提高可读性。通常我会在while语句之前读取一个块,然后在while循环结束时再次读取这些块,这样你就可以使用while chunk:而不需要使用break - undefined

0

不要使用内存占用高(且天真)的方法

import gzip
with gzip.open(zipped_file, 'rb') as zin, open(unzipped_file, 'wb') as wout:
     wout.write(zin.read())

根据之前的答案,我测试了这个:

import gzip
block_size = 64*1024
with gzip.open(zipped_file, 'rb') as zin, open(unzipped_file, 'wb') as wout:
while True:
    uncompressed_block = zin.read(block_size)
    if not uncompressed_block:
        break
    wout.write(uncompressed_block)

已验证通过一个4.8G的文件。


我觉得这个问题肯定已经在其他地方被问过并得到了答案。有人有链接吗? - mohawkTrail
@Vinit提出的解决方案并被Jean-Francois提及是最好的。 - mohawkTrail

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接