如何循环直到Python中的EOF?

时间:2009-11-17 21:53:02

标签: python eof stringio

我需要循环,直到我找到类似文件的对象的结尾,但我找不到“显而易见的方法”,这让我怀疑我忽略了一些东西,很明显。 : - )

我有一个流(在这种情况下,它是一个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不起作用。有没有想过更好的方法来实现这个目标?

6 个答案:

答案 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喜欢:

,我可以用更多的pythonic方式编写
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)