如何拆分大小超过可用内存的文件?

时间:2017-04-10 15:04:34

标签: python io

让我们说我只有8G的堆空间可用,我想将一个比这更大的文件剔除到一系列较小的文件中。如果我试试

with open(fname) as f:
    content = f.readlines()

我将耗尽内存,因为它会尝试加载整个文件。有没有办法打开文件而不将整个内容加载到内存中,只需要从X到Y行?

2 个答案:

答案 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