Python 缓冲区解码

Python buffer decode

我有一个从 C++ 程序发送的 UDP 包,我必须在 Python 中读取它。我设法收到了 UDP 包,但不幸的是它被编码了。 C++ 数据的形式为

struct packet{
    double arg1
    double arg2
    double arg3
    double arg4
    int16_t arg5
};

我收到的数据总共有 42 个字节长(与发送的长度相同)。也许数据在发送之前也略有改变,但我仍在努力弄清楚。 所以我的问题是:让我们假设发送数据的结构与上面描述的完全一样。我怎样才能从缓冲区中读出它? (顺便说一句。我正在使用 Python 2.7.9)

谢谢

编辑:我发现了一些关于结构的东西

void Send(const CrazyFliePacket& packet)
    {
        FMA::ByteBuffer p = 
            FMA::Encode((PACKET_TYPE)FMA_PACKET_TYPES::CRAZYFLIE) + 
            FMA::Encode((PACKET_VERSION)0) +
            FMA::Encode(_seq++) + 
            packet.Encode();

        _server->SendPacket(p.data(),p.size());
        printf("Packet sent with %i bytes size\n",p.size());
    }

类型是:

typedef unsigned short PACKET_TYPE;
typedef unsigned short PACKET_VERSION;
uint16_t _seq;

发送和接收的数据现在是 40 字节长。

Edit2:正确的解决方案是

print struct.unpack('<HHhddddh', data)

我想 struct 就是您要找的。无论如何你必须找出序列化数据格式。发送的不是 C/C++ 结构,而是(希望如此)一种描述良好的序列化格式,可能是二进制文件。重要的主题是 little/big 字节序、double 的大小、int 的大小和填充。

from struct import *
arg1, arg2, arg3, arg4, arg5 = unpack('!ddddi', buffer)

标准尺寸需要 36 个字节

>>> from struct import *
>>> a='\x00\x00\x00\x01'*9
>>> unpack('>ddddi', a)
(2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 1)
>>> unpack('<ddddi', a)
(7.291122046717944e-304, 7.291122046717944e-304, 7.291122046717944e-304, 7.291122046717944e-304, 16777216)
>>> unpack('!ddddi', a)
(2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 1)
>>>

编辑:解包到变量、命名元组和class

直接解压到你的变量

from struct import unpack 
b = "\x00\x00\x00\x05\x00\x00\x00\x2a"

x, y = unpack('!ii', b)
print x
print y

解包到命名元组

from struct import unpack 
from collections import namedtuple
b = "\x00\x00\x00\x05\x00\x00\x00\x2a"

Point = namedtuple('Point', 'x, y')

p = Point._make(unpack("!ii", b))
print p.x
print p.y

记住:元组属性是只读的

解压并初始化 class

from struct import unpack 
b = "\x00\x00\x00\x05\x00\x00\x00\x2a"

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(*unpack('!ii', b))
print p.x
print p.y

我也使用结构解包(并且每次都必须搜索字符代码)直到我学会了使用 ctypes 做同样的事情。我不知道哪一个执行得更快,但我知道我可以毫无问题地翻译哪一个,只要看看 C 定义。

from ctypes import LittleEndianStructure, BigEndianStructure, c_int16, c_double, sizeof
#there are bit explicit types if you want to fix the size, like c_int8/16/32/64

class Packet(LittleEndianStructure): # replace by BigEndian as needed
    _fields_ = [
        ("arg1", c_double),
        ("arg1", c_double),
        ("arg3", c_double),
        ("arg4", c_double),
        ("arg5", c_int16)
        ]
    _pack_=1 # comment this or not depending on padding

mypacket = Packet.from_buffer_copy(payload)

它对我来说更容易阅读,它的优点是为我提供了一个收集所有数据并可以通过名称访问的对象

print(mypacket.arg1)