Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 从BytesIO解包的惯用方法?_Python_Python 3.x - Fatal编程技术网

Python 从BytesIO解包的惯用方法?

Python 从BytesIO解包的惯用方法?,python,python-3.x,Python,Python 3.x,我有一些字节数据希望被解析为流,因为序列中较早的字节控制下游字节的解释。所以BytesIO看起来像我想要的东西。但是我还想使用struct模块提供的工具。但是struct的接口不是流式的。有没有一种聪明/惯用的方法把这两个人结合起来 作为示例,下面是一个数据块示例: b'\n\x00\x02\x90\x10\x00\n\x00\x02`\x10\x00\n\x00\x02\x80\x10\x00' 我想将前4个字节作为无符号的big-endian int(例如,struct.unpack(fm

我有一些字节数据希望被解析为流,因为序列中较早的字节控制下游字节的解释。所以BytesIO看起来像我想要的东西。但是我还想使用struct模块提供的工具。但是struct的接口不是流式的。有没有一种聪明/惯用的方法把这两个人结合起来

作为示例,下面是一个数据块示例:

b'\n\x00\x02\x90\x10\x00\n\x00\x02`\x10\x00\n\x00\x02\x80\x10\x00'
我想将前4个字节作为无符号的big-endian int(例如,
struct.unpack(fmt='>I'
)。因为下一个字节是0x10,我知道应该还有一个字节,结果是0x00。然后它重新开始,读取下一个4(0x0A000290),清洗,漂洗,重复。字节紧接着每个4字节id,触发各种下游读取(一些字节,一些短路)

我可以做像这样的事情

stream = b'\n\x00\x02\x90\x10\x00\n\x00\x02`\x10\x00\n\x00\x02\x80\x10\x00'
while stream:
    id = struct.unpack('>I', stream[:4])
    stream = stream[4:]
    ...

但这似乎不够优雅。

我通常做的是:

def unpack(stream, fmt):
    size = struct.calcsize(fmt)
    buf = stream.read(size)
    return struct.unpack(fmt, buf)
例如:

>>> b = io.BytesIO(b'\n\x00\x02\x90\x10\x00\n\x00\x02`\x10\x00\n\x00\x02\x80\x10\x00')
>>> print(unpack(b, '>I'))
(167772816,)
>>> print(unpack(b, '>I'))
(268438016,)
>>> print(unpack(b, '>I'))
(39849984,)
>>> print(unpack(b, '>I'))
(167772800,)
>>> print(unpack(b, '>H'))
(4096,)
如果你想知道你是否被整个流程所消耗,你可以这样做:

buf = stream.read(1)
if buf:
    raise ValueError("Stream not consumed")
但是调用您已经在使用的相同函数可能更简单:

>>> def ensure_finished(stream):
...     try:
...         unpack(stream, 'c')
...     except struct.error:
...         pass
...     else:
...         raise ValueError('Stream not consumed')
>>> ensure_finished(b)

如果您使用的流读取的字节数可能
小于请求的字节数,则需要使用
while
循环继续读取和追加,直到EOF或获得足够的字节。否则,这就是您所需的全部。

使用
结构
的缓冲区API:

buf = b'\n\x00\x02…'
offset = 0
id = struct.unpack_from('>I', buf, offset); offset += 4
⋮
x = struct.unpack_from('…', buf, offset)
如果希望避免在每次操作后声明偏移量,可以编写一个小包装,如下所示:

class unpacker(object):
    def __init__(self, buf):
        self._buf = buf
        self._offset = 0
    def __call__(self, fmt):
        result = struct.unpack_from(fmt, self._buf, self._offset)
        self._offset += struct.calcsize(fmt)
        return result

⋮

unpack = unpacker(buf)
id = unpack('>I')
⋮
x = unpack('…')

这实际上比使用切片更不优雅;如果您以后在位置4和8之间添加一个新的int,您必须更新以下所有偏移量。此外,从您的编辑来看,没有像
buffer
这样的类型。我不确定您要找的是什么类型;
bytes
已经很好地支持buffer API了。@abarnet:对不起,我假设Python2.x。更新后的版本现在并不比OP的原始代码差,但也不是更好。您仍然需要手动将偏移量与格式匹配,这很容易出错,也很难调试。假设“优雅”意味着“高效”,这总是一种延伸。而OP明确要求“巧妙或惯用的方式合并”一个
BytesIO
-类似流和
struct
,以避免让用户单独或并行地指定长度和格式,这非常强烈地暗示了他在本例中所说的“优雅”。好吧,这可能是我将采用的答案。有一个简单的(内置的)方法吗当我在流的末尾时如何进行测试?如果不是,我想我也可以为此做一个助手(根据底层序列的len()测试当前位置)。@TravisGriggs:a
read()
返回0字节时,你就在流的末尾。Bummer。这意味着我不能在调用unpack()之前进行测试如果我完成了或没有完成,而是必须调用unpack(),捕获0大小写读取,并引发异常或返回None,然后在调用端处理该异常。我曾希望能够执行类似于
的操作,而不是io.atEnd():unpack(),unpack(),unpack()…
实现
atEnd(stream):return stream.tell()==len(stream.getbuffer())
?@TravisGriggs:这是Python类文件对象的一个普遍问题:它们旨在以EAFP风格使用,即在达到EOF之前永远循环,而不是在EOF测试之前循环。但是如果您确实想要测试,您可以用与其他任何方法相同的方法进行。只需使用
BytesIO
,您的方法就可以了。更一般地说,您可以这样做
stream.seek(0,io.seek_END);streamlen=stream.tell();stream.seek(0)
开始时,只需在stream.tell()时比较