我需要循环,直到我找到类似文件的对象的结尾,但我找不到“显而易见的方法”,这让我怀疑我忽略了一些东西,很明显。 : - )
我有一个流(在这种情况下,它是一个StringIO对象,但我对一般情况也很好奇),它在“< length>< data>”中存储了未知数量的记录格式,例如:
data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00")
现在,我能想象的唯一清晰的方法就是使用(我想到的)一个初始化的循环,这看起来有点像非Pythonic:
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)
在类似C语言中,我只是将read(4)
粘贴在while
的测试子句中,但当然这对Python不起作用。有没有想过更好的方法来实现这个目标?
答案 0 :(得分:26)
您可以将iter()的迭代与哨兵:
结合起来for block in iter(lambda: file_obj.read(4), ""):
use(block)
答案 1 :(得分:10)
您是否看过如何迭代文本文件中的行?
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)
另见:
答案 2 :(得分:5)
我更喜欢已经提到的基于迭代器的解决方案将其转换为for循环。直接写的另一个解决方案是Knuth的“循环半”
while 1:
len_name = data.read(4)
if not len_name:
break
names.append(data.read(len_name))
您可以通过比较看到它如何轻松地升级到自己的生成器并用作for循环。
答案 3 :(得分:3)
我看到,正如预测的那样,典型且最受欢迎的答案是使用非常专业的生成器来“一次读取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
现在您想要的循环标题只是:for len_name in funlooper(data.read, 4):
。
修改:由wearedone
成语更加通用,因为评论指责我稍微不那么一般的先前版本(硬编码退出测试为if not data:
)隐藏的依赖“,所有事情! - )
循环的普通瑞士军刀,itertools
,当然也像往常一样很好:
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): ...
答案 4 :(得分:1)
python中的EOF标记是一个空字符串,所以你所拥有的非常接近你将要获得的最佳效果,而无需编写函数将其包装在迭代器中。通过改变while
喜欢:
while len_name:
len_name = struct.unpack("<I", len_name)[0]
names.append(data.read(len_name))
len_name = data.read(4)
答案 5 :(得分:0)
我会选择Tendayi的建议re函数和迭代器以提高可读性:
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)