为啥 takewhile() 会跳过第一行?

Posted

技术标签:

【中文标题】为啥 takewhile() 会跳过第一行?【英文标题】:Why does takewhile() skip the first line?为什么 takewhile() 会跳过第一行? 【发布时间】:2011-09-02 01:13:30 【问题描述】:

我有一个这样的文件:

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],为什么?

【问题讨论】:

for 循环正在吃掉每个块的第一行 【参考方案1】:

这是使用 groupby 的另一种方法

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

【讨论】:

嗨@gnibbler,您的代码可能适用于小文件。我有一个非常大的文件,我不想一次阅读所有文件。但是感谢您的代码。 @gstar,你为什么认为我的代码会一次读取整个文件? 为什么不只是for x, y in groupby(lines, "\t\n".__ne__): if x: yield list(y)? (我要说“为什么不只返回生成器表达式”,但我猜这会导致上下文管理器过早触发......)(我惊讶地发现groupby 不会用相同的键整理组。 ..) @Karl,是的,我认为这样更好。是的,您需要在 with 块内让步以防止文件过早关闭 @gnibbler,真正的 groupby() 应该是整个列表上的全局操作,它必须以某种方式首先读取所有行。但是你在这里,pythong 中的 groupby() 是一个变化检测器.【参考方案2】:

给你,测试代码。使用while True: 循环,并让itertools.takewhile() 使用lines 做所有事情。当itertools.takewhile() 到达输入的末尾时,它返回一个迭代器,除了raise 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 both 作为组结束的标记以及在文件结束时获得的标记。更糟糕的是,我找不到任何方法来询问文件迭代器对象是否已到达文件结尾。更糟糕的是,itertools.takewhile() 似乎将文件迭代器立即推进到文件结尾;当我尝试使用lines.tell() 重写上述内容以检查我们的进度时,它已经在第一组之后的文件末尾。

我建议使用itertools.groupby() 解决方案。它更干净。

【讨论】:

太好了,我应该尝试使用您的代码。谢谢。我不知道,Regex 是否也可以完成这项工作。 连续2行TAB不会也创建一个空块吗? @Paul McGuire,这是一个非常好的观点。我认为itertools.groupby() 的答案更干净,没有这个缺陷。【参考方案3】:

我认为问题在于您在 lambda 函数中使用了 lines 而不是 line。您的预期输出是什么?

【讨论】:

【参考方案4】:

itertools.takewhile 隐式迭代文件的lines 以获取块,但for line in lines: 也是如此。每次通过循环,一个line 被抓取,丢弃(因为没有使用line 的代码),然后更多的blocked 在一起。

【讨论】:

嗨,卡尔,我已经考虑过了。在第一个 takewhile() 之后,文件指针指向 TAB 行,在我处理了第一个块之后,“for”将文件指针移动到下一行,'1' 并将其交给 takewhile()。应该是对的。但是…… for 循环不会“移动文件指针”;这是错误的思考方式。它遍历文件的行。第一次通过循环,line 等于 '1\n'。该值已被消耗,takewhile() 不再可用。 好的,我明白了。所以'code'takewhile() 消耗了TAB 行。然后 'code' 消耗 '1\n' 行,所以 'code'takewhile() 从 '2\n' 获取行。很棒。

以上是关于为啥 takewhile() 会跳过第一行?的主要内容,如果未能解决你的问题,请参考以下文章

BufferedReader 跳过第一行

DataReader.Read() 跳过记录集的第一行

LOAD DATA LOCAL,如何跳过第一行?

使用 CSV 文件在循环中跳过第一行(字段)? [复制]

sh AWK跳过第一行

从串口写入数据时如何跳过第一行?