Python如何一次读取N行

62 投票
7 回答
59317 浏览
提问于 2025-04-16 19:29

我正在写一段代码,目的是处理一个非常大的文本文件(几GB),每次处理N行,然后再处理下一批N行,直到整个文件都处理完。 (我不在乎最后一批的行数是不是正好)。

我在研究如何使用itertools库中的islice来完成这个操作。我觉得我已经有了一半的思路:

from itertools import islice
N = 16
infile = open("my_very_large_text_file", "r")
lines_gen = islice(infile, N)

for lines in lines_gen:
     ...process my lines...

问题是,我想处理下一批16行,但我似乎缺少了一些东西。

7 个回答

3

这里有另一种方法,使用 groupby

from itertools import count, groupby

N = 16
with open('test') as f:
    for g, group in groupby(f, key=lambda _, c=count(): c.next()/N):
        print list(group)

它是如何工作的:

简单来说,groupby() 会根据一个叫做 key 的参数的返回值来对行进行分组。这个 key 参数是一个 lambda 函数,具体是 lambda _, c=count(): c.next()/N。这里的 c 参数会在定义这个 函数时 绑定到 count() 上。因此,每次调用 groupby() 时,都会调用这个 lambda 函数,并计算它的返回值,以确定如何对行进行分组。所以:

# 1 iteration.
c.next() => 0
0 / 16 => 0
# 2 iteration.
c.next() => 1
1 / 16 => 0
...
# Start of the second grouper.
c.next() => 16
16/16 => 1   
...
11

这个问题似乎假设通过一次读取“N行”的方式来处理一个“巨大的文本文件”可以提高效率。其实,这样做只是给已经非常优化的stdio库增加了一层额外的缓冲,增加了复杂性,而且可能根本没有带来任何好处。

所以:

with open('my_very_large_text_file') as f:
    for line in f:
        process(line)

在时间、空间、复杂性和可读性方面,这种方式可能是最优的选择。

另外,可以参考Rob Pike的前两条规则Jackson的两条规则,以及PEP-20 Python之禅。如果你只是想玩玩islice,那么就不应该提到处理大文件的事情。

81

islice() 是一个可以用来获取迭代器下一个 n 个项目的工具。也就是说,使用 list(islice(f, n)) 可以从文件 f 中获取接下来的 n 行内容。把这个放在循环里使用,就能把文件分成 n 行一块一块地读取。当文件读到最后时,返回的列表可能会短一些,最后一次调用会返回一个空列表。

from itertools import islice
with open(...) as f:
    while True:
        next_n_lines = list(islice(f, n))
        if not next_n_lines:
            break
        # process next_n_lines

另一种方法是使用 grouper 模式

from itertools import zip_longest
with open(...) as f:
    for next_n_lines in zip_longest(*[f] * n):
        # process next_n_lines

撰写回答