Python如何一次读取N行
我正在写一段代码,目的是处理一个非常大的文本文件(几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 个回答
这里有另一种方法,使用 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
...
这个问题似乎假设通过一次读取“N行”的方式来处理一个“巨大的文本文件”可以提高效率。其实,这样做只是给已经非常优化的stdio
库增加了一层额外的缓冲,增加了复杂性,而且可能根本没有带来任何好处。
所以:
with open('my_very_large_text_file') as f:
for line in f:
process(line)
在时间、空间、复杂性和可读性方面,这种方式可能是最优的选择。
另外,可以参考Rob Pike的前两条规则、Jackson的两条规则,以及PEP-20 Python之禅。如果你只是想玩玩islice
,那么就不应该提到处理大文件的事情。
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