如何在Python中循环直到EOF?

发布于 2024-08-11 03:40:18 字数 623 浏览 13 评论 0原文

我需要循环,直到到达类似文件的对象的末尾,但我没有找到“明显的方法”,这让我怀疑我忽略了一些明显的东西。 :-)

我有一个流(在本例中,它是一个 StringIO 对象,但我也对一般情况感到好奇),它在“”中存储未知数量的记录格式,例如:

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)

在类似 C 的语言中,我只需坚持while 的测试子句中的 read(4) ,但这当然不适用于 Python。关于更好的方法来实现这一目标有什么想法吗?

I need to loop until I hit the end of a file-like object, but I'm not finding an "obvious way to do it", which makes me suspect I'm overlooking something, well, obvious. :-)

I have a stream (in this case, it's a StringIO object, but I'm curious about the general case as well) which stores an unknown number of records in "<length><data>" format, e.g.:

data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00")

Now, the only clear way I can imagine to read this is using (what I think of as) an initialized loop, which seems a little un-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)

In a C-like language, I'd just stick the read(4) in the while's test clause, but of course that won't work for Python. Any thoughts on a better way to accomplish this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

迷爱 2024-08-18 03:40:18

您可以将通过 iter() 的迭代与哨兵结合起来:

for block in iter(lambda: file_obj.read(4), ""):
  use(block)

You can combine iteration through iter() with a sentinel:

for block in iter(lambda: file_obj.read(4), ""):
  use(block)
好倦 2024-08-18 03:40:18

您是否了解过如何迭代文本文件中的行?

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)

另请参阅:

Have you seen how to iterate over lines in a text file?

for line in file_obj:
  use(line)

You can do the same thing with your own generator:

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)

See also:

情仇皆在手 2024-08-18 03:40:18

我更喜欢已经提到的基于迭代器的解决方案,将其转换为 for 循环。另一个直接编写的解决方案是 Knuth 的“循环半”。

while 1:
    len_name = data.read(4)
    if not len_name:
        break
    names.append(data.read(len_name))

通过比较,您可以看到它如何轻松地提升到自己的生成器中并用作 for 循环。

I prefer the already mentioned iterator-based solution to turn this into a for-loop. Another solution written directly is Knuth's "loop-and-a-half"

while 1:
    len_name = data.read(4)
    if not len_name:
        break
    names.append(data.read(len_name))

You can see by comparison how that's easily hoisted into its own generator and used as a for-loop.

生生不灭 2024-08-18 03:40:18

正如预测的那样,我看到典型且最流行的答案是使用非常专业的生成器“一次读取 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): ...

I see, as predicted, that the typical and most popular answer are using very specialized generators to "read 4 bytes at a time". Sometimes generality isn't any harder (and much more rewarding;-), so, I've suggested instead the following very general solution:

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

Now your desired loop header is just: for len_name in funlooper(data.read, 4):.

Edit: made much more general by the wearedone idiom since a comment accused my slightly less general previous version (hardcoding the exit test as if not data:) of having "a hidden dependency", of all things!-)

The usual swiss army knife of looping, itertools, is fine too, of course, as usual:

import itertools as it

for len_name in it.takewhile(bool, it.imap(data.read, it.repeat(4))): ...

or, quite equivalently:

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): ...
自由如风 2024-08-18 03:40:18

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)

The EOF marker in python is an empty string so what you have is pretty close to the best you are going to get without writing a function to wrap this up in an iterator. I could be written in a little more pythonic way by changing the while like:

while len_name:
    len_name = struct.unpack("<I", len_name)[0]
    names.append(data.read(len_name))
    len_name = data.read(4)
离鸿 2024-08-18 03:40:18

为了提高可读性,我会采纳 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)

I'd go with Tendayi's suggestion re function and iterator for readability:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文