在Python中如何循环到EOF?
我需要循环,直到找到一个类似文件的对象的结尾,但我没有找到“显而易见的方法”,这让我怀疑我忽略了什么,嗯,显而易见的。:-) 我有一个流(在本例中,它是一个StringIO对象,但我对一般情况也很好奇),它以“”格式存储未知数量的记录,例如:在Python中如何循环到EOF?,python,eof,stringio,Python,Eof,Stringio,我需要循环,直到找到一个类似文件的对象的结尾,但我没有找到“显而易见的方法”,这让我怀疑我忽略了什么,嗯,显而易见的。:-) 我有一个流(在本例中,它是一个StringIO对象,但我对一般情况也很好奇),它以“”格式存储未知数量的记录,例如: data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00") 现在,我能想象的阅读这篇文章的唯一清晰方式是使用(我认为是)一个初始化的循环,这似乎有点不符合python: len
data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00")
现在,我能想象的阅读这篇文章的唯一清晰方式是使用(我认为是)一个初始化的循环,这似乎有点不符合python:
len_name = data.read(4)
while len_name != "":
len_name = struct.unpack("<I", len_name)[0]
names.append(data.read(len_name))
len_name = data.read(4)
len_name=data.read(4)
而len_name!="":
len_name=struct.unpack(“您见过如何迭代文本文件中的行吗
for line in file_obj:
use(line)
您可以使用自己的生成器执行相同的操作:
def read_blocks(file_obj, size):
while True:
data = file_obj.read(size)
if not data:
break
yield data
for block in read_blocks(file_obj, 4):
use(block)
另见:
我发现,正如预测的那样,最典型和最流行的答案是使用非常专业的生成器“一次读取4个字节”。有时通用性并不难(也更值得;-),因此,我建议使用以下非常通用的解决方案:
import operator
def funlooper(afun, *a, **k):
wearedone = k.pop('wearedone', operator.not_)
while True:
data = afun(*a, **k)
if wearedone(data): break
yield data
现在,您所需的循环头只是:funlooper中lenu name的(data.read,4):
编辑:由于一条评论指责我的前一个版本(将退出测试硬编码为如果不是数据:
)有“隐藏的依赖关系”,wearedone
的习惯用法变得更加通用了
通常的瑞士军刀循环,也很好,当然,像往常一样:
import itertools as it
for len_name in it.takewhile(bool, it.imap(data.read, it.repeat(4))): ...
或者,相当等价地:
import itertools as it
def loop(pred, fun, *args):
return it.takewhile(pred, it.starmap(fun, it.repeat(args)))
for len_name in loop(bool, data.read, 4): ...
python中的EOF标记是一个空字符串,因此,如果不编写函数将其封装在迭代器中,您所拥有的非常接近您将获得的最佳值。我可以通过更改
while
以一种更加python的方式编写,如:
while len_name:
len_name = struct.unpack("<I", len_name)[0]
names.append(data.read(len_name))
len_name = data.read(4)
while len_name:
len_name=struct.unpack(“我更喜欢前面提到的基于迭代器的解决方案,将其转换为for循环。另一个直接编写的解决方案是Knuth的“循环半”
通过比较,您可以看到它是如何轻松地提升到自己的生成器中并用作for循环的。您可以将迭代与哨兵结合起来:
for block in iter(lambda: file_obj.read(4), ""):
use(block)
我同意Tendayi关于函数和迭代器可读性的建议:
def read4():
len_name = data.read(4)
if len_name:
len_name = struct.unpack("<I", len_name)[0]
return data.read(len_name)
else:
raise StopIteration
for d in iter(read4, ''):
names.append(d)
def read4():
len_name=data.read(4)
如果len_名称:
len_name=struct.unpack("您还可以在生成器中将循环构造为while循环。使用可读性最好的方法。虽然存在隐藏的依赖项,但由于funlooper要求函数返回非真结果以指示结束。@R.Pate,您当然可以简单地向funlooper添加一个默认为运算符的wearedone
谓词参数。不是_
并将if
更改为if wearedone(数据):break
——我只是觉得不值得用这个琐碎的代码进一步概括答案,因为我确信(而且是正确的;-)其他答案会过于专业化(毫无益处).啊,好吧,既然过度的专业化正在赢得这一天的胜利,让我编辑一下答案,以表明在这种情况下,通用性并没有变得更难(也更值得一试;-).我想你误解了我的意思:最初的funlooper太笼统了。因为我们已经依赖具有特定形式的返回值,所以在这里依赖类似文件的接口(read方法)的这一部分是合理的,而不是试图传递一个通用的可调用函数。如果失败,用户必须至少知道依赖关系。“Boolean false”不是“一种特定的形式”——它是一种非常通用的形式,许多种类的Python对象都可以满足。您对这个问题的两个答案之一(!),一个未被接受的,目前投票数最多的,和我原来的一样长和一般结构(按照正常的SO礼仪,我不会对一个问题给出很多答案!),所以它的极端专业化并没有带来任何好处。(你们中的另一个答案,被iter和sentinel接受的答案,更简洁——没有我的itertools那么笼统,但更简单)。如果你不同意它取决于返回值的特定形式,为什么你要更改答案?你认为依赖性的其他方式是什么?(我最初更喜欢你的答案。)绝对是最好的anwser。你让我在这一个,我忘记了这个非常有用的哨兵。我想我也最喜欢这一个;它的作用非常清楚,因为代码太少了。谢谢你的帮助!只是提醒你,如果你一直在写它,在迭代它之前,友好地回放文件,即file_obj.seek(0)。这需要在循环之前将赋值复制到len_name(您省略了),并且几乎总是希望避免这种重复。答案太多了!也许我不应该在午餐前发布。:-)在这种情况下,我想我喜欢iter()
解决方案更好,但我觉得没有想到这一点很愚蠢。这是你应得的+1。-)哇。是的,iter()解决方案很好。再加上“lambda:”和闭包,这让理解起来有点困难,但绝对是甜蜜的。没有理由,只是我很快就拼凑起来的东西。我已经修改了这个片段。
def read4():
len_name = data.read(4)
if len_name:
len_name = struct.unpack("<I", len_name)[0]
return data.read(len_name)
else:
raise StopIteration
for d in iter(read4, ''):
names.append(d)