【问题标题】:Python 3: for x in bytes as wordsPython 3:以字节为单位的 x 作为单词
【发布时间】:2021-03-24 12:00:11
【问题描述】:

我有一个 python3 脚本,它将数据读入缓冲区

fp = open("filename", 'rb')
data = fp.read(count)

我不完全理解(即使在阅读了文档之后)read() 返回的内容。它似乎是某种可迭代的二进制数据。但它不是一个列表。

令人困惑的是,在脚本的其他地方,列表用于二进制数据。

frames = []
# then later... inside a loop
for ...
    data = b''.join(frames)

无论如何...我想以字为单位(又名 2 字节块)迭代 read() 返回的对象

目前脚本包含这个 for 循环

for c in data:
    # do something

是否可以更改 c 以使此循环遍历单词(2 字节块)而不是单个字节?

编辑:

我不能在循环中使用 read() 一次读取 2 个字节。

【问题讨论】:

  • “我不完全理解(即使在阅读文档之后)read() 返回的内容。它似乎是某种可迭代的二进制数据。但它不是一个列表。” this 是您找到的文档吗?上面写着Files opened in binary mode (including 'b' in the mode argument) return contents as bytes objects without any decodingbytes 这个词链接到该类型的文档。
  • “令人困惑的是,在脚本的其他地方,列表用于二进制数据。”使用了 一个 列表,其中可能包含使用b''join 方法连接的bytes 对象(另一个bytes 对象)。
  • 您希望c 是什么类型的对象?在其当前形式中,c 将是 int。然而,在“2 字节块”中,它将是一个 bytes 对象。
  • @KarlKnechtel 这似乎是open() 的文档?不是read()
  • 是的,这是open() 的文档。 open() 是您创建文件对象的方式,那里的文档解释了您可以使用这些对象做什么。 .read() 是文件对象的一种方法,因此您可以从那里获得相应的文档。

标签: python-3.x binary-data


【解决方案1】:

我们可以使用 .read(n) 从二进制模式的文件中显式读取(最多)n 字节(就像它会从以文本模式打开的文件中读取 n Unicode 代码点一样)。这是一个阻塞调用,只会在文件末尾读取更少的字节。

我们可以使用iter 的两个参数形式来构建一个重复调用可调用对象的迭代器:

>>> help(iter)
Help on built-in function iter in module builtins:

iter(...)
    iter(iterable) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

文件末尾的read 将开始返回空结果并且不会引发异常,因此我们可以将其用于我们的哨兵。

把它放在一起,我们得到:

for pair in iter(lambda: fp.read(2), b''):

在循环中,我们将获得代表两个字节数据的bytes 对象。您应该查看文档以了解如何使用它们。

【讨论】:

    【解决方案2】:

    以二进制模式读取文件时,会返回一个bytes 对象,这是标准的python 内置函数之一。一般来说,它在代码中的表示形式看起来像一个字符串,除了它的前缀为 b" " - 当您尝试打印它时,每个字节可能会显示为像 \x** 这样的转义符,其中 ** 是 2 个十六进制对应于从 0 到 255 的字节值的数字,或直接作为单个可打印的 ascii 字符,具有与数字相同的 ascii 代码点。你可以阅读更多关于这个和字节的方法等(也类似于字符串)in the bytes docs

    似乎已经有a very popular question on stack overflow about how to iterate over a bytes objectcurrently accepted answer 给出了在 bytes 对象中创建单个字节的 list 的示例:

    L = [bytes_obj[i:i+1] for i in range(len(bytes_obj))]
    

    我想像这样修改它会对你有用:

    L = [bytes_obj[i:i+2] for i in range(0, len(bytes_obj), 2)]
    

    例如:

    by = b"\x00\x01\x02\x03\x04\x05\x06" 
    # The object returned by file.read() is also bytes, like the one above
    words = [by[i:i+2] for i in range(0, len(by), 2)]
    print(words)
    # Output --> [b'\x00\x01', b'\x02\x03', b'\x04\x05', b'\x06']
    

    如果您的列表可能太大而无法一次有效地存储,请创建一个生成器以相同方式生成单词:

    def get_words(bytesobject):
        for i in range(0, len(bytesobject), 2):
            yield bytesobject[i:i+2]
    

    【讨论】:

    • 好的,谢谢,这非常有用。看来我实际上是在问错误的问题。也许我应该问,“为什么迭代字节对象返回ints”
    • "也许我应该问,"为什么迭代字节对象返回整数"因为bytes 对象代表一个字节序列内存,最简单的方法将 8 位值解释为 8 位无符号整数?对我来说似乎是正确的。当然我过去发现它很有用。我不确定你还期望什么;它不是字符串类型(在 3 .x;坦率地说,2.x 的设计存在很大缺陷)。
    【解决方案3】:

    从最简单的字面意义上说,这样的事情一次给你一个两个字节的循环。

    with open("/etc/passwd", "rb") as f:
        w = f.read(2)
        while len(w) > 0:
            print( w  )
            w = f.read(2)
    

    至于你从read 得到什么,它是一个bytes 对象,因为你已经将'b' 指定为'open' 的一个选项

    我认为更 Python 的表达方式是通过迭代器或生成器。

    【讨论】:

      猜你喜欢
      • 2013-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-03
      • 1970-01-01
      • 1970-01-01
      • 2016-11-07
      • 1970-01-01
      相关资源
      最近更新 更多