17

所以我有一些相当大的 .gz 文件——解压后每个文件 10 到 20 GB。

我需要遍历它们的每一行,所以我使用的是标准:

import gzip
f = gzip.open(path+myFile, 'r')
for line in f.readlines():
    #(yadda yadda)
f.close()

但是,open()close()命令都占用了 AGES,占用了 98% 的内存+CPU。以至于程序退出并打印Killed到终端。也许它将整个提取的文件加载到内存中?

我现在正在使用类似的东西:

from subprocess import call
f = open(path+'myfile.txt', 'w')
call(['gunzip', '-c', path+myfile], stdout=f)
#do some looping through the file
f.close()
#then delete extracted file

这行得通。但是有更清洁的方法吗?

4

2 回答 2

59

我 99% 确定您的问题gzip.open()不在readlines().

正如文档所解释的:

f.readlines() 返回一个包含文件中所有数据行的列表。

显然,这需要阅读和解压整个文件,并建立一个绝对庞大的列表。

最有可能的是,实际上是malloc分配所有内存的调用将永远占用。然后,在这个范围的末尾(假设您使用的是 CPython),它必须对整个巨大的列表进行 GC,这也将花费很长时间。

你几乎从不想使用readlines. 除非您使用的是非常旧的 Python,否则请执行以下操作:

for line in f:

Afile是一个完整的可迭代行,就像 —list返回的readlines一样,除了它实际上不是 a 之外list,它通过读取缓冲区来动态生成更多行。因此,在任何给定时间,您将只有一行和几个缓冲区,每个缓冲区大约 10MB,而不是 25GB list。并且读取和解压缩将在循环的整个生命周期中展开,而不是一次完成。

通过快速测试,使用 3.5GB 的 gzip 文件,gzip.open()实际上是即时的,for line in f: pass需要几秒钟,gzip.close()实际上是即时的。但是如果我这样做for line in f.readlines(): pass了,它需要……好吧,我不确定要多长时间,因为大约一分钟后,我的系统进入了交换系统的颠簸地狱,我不得不强行杀死解释器以使其响应任何事情……</p >


由于这个答案已经出现了十几次,我写了这篇博客文章,解释了更多。

于 2013-02-01T22:26:12.803 回答
2

看看pandas,尤其是 IO tools。它们在读取文件时支持 gzip 压缩,您可以分块读取文件。此外,pandas 速度非常快且内存效率很高。

因为我从未尝试过,我不知道压缩和读取块的效果如何,但可能值得一试

于 2013-02-01T22:28:36.340 回答