为啥 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
的代码),然后更多的block
ed 在一起。
【讨论】:
嗨,卡尔,我已经考虑过了。在第一个 takewhile() 之后,文件指针指向 TAB 行,在我处理了第一个块之后,“for”将文件指针移动到下一行,'1' 并将其交给 takewhile()。应该是对的。但是…… for 循环不会“移动文件指针”;这是错误的思考方式。它遍历文件的行。第一次通过循环,line
等于 '1\n'
。该值已被消耗,takewhile()
不再可用。
好的,我明白了。所以'code'takewhile() 消耗了TAB 行。然后 'code' 消耗 '1\n' 行,所以 'code'takewhile() 从 '2\n' 获取行。很棒。以上是关于为啥 takewhile() 会跳过第一行?的主要内容,如果未能解决你的问题,请参考以下文章