所以我有一些相当庞大的.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
这很有效。但是有更干净的方式吗?
答案 0 :(得分:57)
我99%确定您的问题不在gzip.open()
,而在readlines()
。
正如the documentation所解释的那样:
f.readlines()返回一个包含文件中所有数据行的列表。
显然,这需要阅读和解压缩整个文件,并建立一个绝对巨大的列表。
最有可能的是,它实际上是malloc
调用来分配永远占用的所有内存。然后,在这个范围的最后(假设您正在使用CPython),它必须GC到整个巨大的列表,这也将永远。
您几乎不想使用readlines
。除非您使用的是非常旧的Python,否则请执行以下操作:
for line in f:
file
是一个可迭代的行,就像list
返回的readlines
一样 - 除了它实际上不是list
之外,它会生成更多行通过读取缓冲区飞行。因此,在任何给定时间,您将只有一行和几个大小为10MB的缓冲区,而不是25GB list
。读取和解压缩将在循环的生命周期内展开,而不是一次完成。
通过快速测试,使用3.5GB gzip文件,gzip.open()
实际上是即时的,for line in f: pass
需要几秒钟,gzip.close()
实际上是即时的。但是,如果我做for line in f.readlines(): pass
,它需要......好吧,我不知道多久,因为大约一分钟后我的系统进入交换颠簸地狱,我不得不强行杀死解释器让它响应什么...
由于这个答案已经出现了十几次,我写了this blog post,这解释了更多。
答案 1 :(得分:2)
看看pandas, in particular IO tools。它们在读取文件时支持gzip压缩,您可以以块的形式读取文件。此外,大熊猫非常快,内存效率很高。
正如我从未尝试过的那样,我不知道块中的压缩和读取是如何共存的,但它可能值得一试