让我们说我只有8G的堆空间可用,我想将一个比这更大的文件剔除到一系列较小的文件中。如果我试试
with open(fname) as f:
content = f.readlines()
我将耗尽内存,因为它会尝试加载整个文件。有没有办法打开文件而不将整个内容加载到内存中,只需要从X到Y行?
答案 0 :(得分:2)
文件句柄可以用作文件中行的迭代器。你想要的是来自迭代器的特定切片。标准库中有一个方便的itertools.islice()
函数,可以完成此操作。
from itertools import islice
line_slice = (10, 20)
with open(fname) as f:
content = islice(f, *line_slice)
上述内容或多或少等同于f.readlines()[10:20]
。
注意islice()
的输出是另一个迭代器。幸运的是writelines()
接受迭代器,因此无需将其转换为临时列表。这实际上也意味着,如果直接将其传递给writelines()
,您将不会在内存中持有多行。
with open(out_fname, 'w') as f:
f.writelines(content)
答案 1 :(得分:1)
itertools.islice
是一个很好的工具,但你需要考虑如何有效地使用它。例如,islice(f, 10, 20)
丢弃10行然后发出20行,因此这不是写入的好方法。根据您编写循环的方式,您可以删除数据或重新扫描每次写入的文件。
知道什么时候完成也不明显。 fileobj.writelines(isslice(f, 10))
会很乐意写0行文件,直到时间结束。你真的只知道你事后已经完成了,所以你可以测试你是否写了一个零长度的文件来终止。
在这个例子中,我的大文件是100行长,我分成10行apeice ....测试比8gig文件快一点。
import itertools
import os
lines_per_file = 10
with open('big.txt') as infp:
# file counter used to create unique output files
for file_count in itertools.count(1):
out_filename = 'out-{}.txt'.format(file_count)
with open(out_filename, 'w') as outfp:
# write configured number of lines to file
outfp.writelines(itertools.islice(infp, lines_per_file))
# break when no extra data written
if os.stat(out_filename).st_size == 0:
os.remove(out_filename)
break