为什么takewhile()跳过第一行?
我有一个这样的文件:
1
2
3
TAB
1
2
3
TAB
我想把TAB之间的行当作一个个块来读取。
import itertools
def block_generator(file):
with open(file) as lines:
for line in lines:
block = list(itertools.takewhile(lambda x: x.rstrip('\n') != '\t',
lines))
yield block
我想这样使用它:
blocks = block_generator(myfile)
for block in blocks:
do_something(block)
我得到的块都是从第二行开始,比如 [2,3] [2,3]
,这是为什么呢?
4 个回答
1
我觉得问题出在你在 lambda 函数里用的是 lines
,而不是 line
。你期望的输出是什么呢?
2
这是经过测试的代码。它使用了 while True:
来循环,并让 itertools.takewhile()
来处理 lines
的所有操作。当 itertools.takewhile()
处理到输入的末尾时,它会返回一个只会抛出 StopIteration
的迭代器,而 list()
会把这个抛出的结果变成一个空列表,所以简单的 if not block:
检查可以检测到这个空列表,并让循环停止。
import itertools
def not_tabline(line):
return '\t' != line.rstrip('\n')
def block_generator(file):
with open(file) as lines:
while True:
block = list(itertools.takewhile(not_tabline, lines))
if not block:
break
yield block
for block in block_generator("test.txt"):
print "BLOCK:"
print block
正如下面评论中提到的,这里有一个缺陷:如果输入文本中有两行连续只有制表符,这个循环会在没有读取完所有输入文本的情况下就停止处理。而我想不出有什么好的方法来处理这个问题;很遗憾的是,从 itertools.takewhile()
返回的迭代器同时使用 StopIteration
作为一组的结束标记和文件结束时的标记。更糟糕的是,我找不到任何方法来询问文件迭代器对象是否已经到达文件末尾。而且更糟的是,itertools.takewhile()
似乎会立即把文件迭代器推进到文件末尾;当我尝试重写上面的代码来检查进度时,使用 lines.tell()
发现它在第一组之后就已经到达文件末尾了。
我建议使用 itertools.groupby()
的解决方案,这样会更简洁。
4
这里有另一种使用分组的方法
from itertools import groupby
def block_generator(filename):
with open(filename) as lines:
for pred,block in groupby(lines, "\t\n".__ne__):
if pred:
yield block