为什么takewhile()跳过第一行?

3 投票
4 回答
1457 浏览
提问于 2025-04-17 01:10

我有一个这样的文件:

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

撰写回答