BigEndianStructure中的字符串

时间:2013-07-09 22:09:35

标签: python ctypes endianness

我想做那样的事情:

from ctypes import *

class Packet(BigEndianStructure):
    _fields_ = [("length", c_ushort),
                ("session", c_uint),
                ("command", c_ushort)]

class PacketString(BigEndianStructure):
    _fields_ = [("length", c_ushort),
                ("value", c_char_p)]

class InitialPacket(Packet):
    _fields_ = [("time", PacketString)]

但是我收到错误,因为c_char_p只能是本机字节顺序。但是也许还有其他一些方法可以让我的字符串长度在它们之前指定。我只是喜欢结构如何易于读取/写入套接字。以及如何定义_fields_然后可以像这样使用它:

initialPacket = InitialPacket()
initialPacket.command = 128

问题是:如何在BigEndianStructure中创建可变长度字段?因为Python不允许我使用c_char_p。脚本根本不会运行。这是错误:

Traceback (most recent call last):
  File "C:\PKOEmu\test.py", line 8, in <module>
    class PacketString(BigEndianStructure):
  File "C:\Python27\lib\ctypes\_endian.py", line 34, in __setattr__
    fields.append((name, _other_endian(typ)) + rest)
  File "C:\Python27\lib\ctypes\_endian.py", line 24, in _other_endian
    raise TypeError("This type does not support other endian: %s" % typ)
TypeError: This type does not support other endian: <class 'ctypes.c_char_p'>

1 个答案:

答案 0 :(得分:2)

此类型:

class PacketString(BigEndianStructure):
    _fields_ = [("length", c_ushort),
                ("value", c_char_p)]

...不会做你认为它做的事情,甚至忽略了字节序问题。它是一个包含ushort长度的结构,然后是指向内存中其他位置的实际字符串数据的指针。

换句话说,它就像这个C结构:

struct PacketString {
    unsigned short length;
    char *value;
};

您正在寻找的是一个长度为前缀的字符串,其中字符串直接在结构内部。为此,C结构是:

struct PacketString {
    unsigned short length;
    char value[1];
};

这称为“struct hack”。这实际上不是合法的C,但它恰好适用于每个已知的C89编译器,以及大多数C99和C ++编译器。请参阅C FAQ entry详细信息。

那么,你能在ctypes做同样的事情吗?嗯,是的,但它没那么有用:

class PacketString(BigEndianStructure):
    _fields_ = [("length", c_ushort),
                ("value", c_char * 0)]

这可能变得复杂;有关详细信息,请参阅文档中的Variable-sized data types。特别是,您无法在resize上致电p.value;您需要计算调整p本身的大小,然后将p._fields_[1]的类型更改为具有正确类型,然后...

嗯,这就是文档说的原因:

  

使用带有ctypes的可变大小数据类型的另一种方法是使用Python的动态特性,并根据具体情况(重新)定义所需大小后的数据类型。

换句话说:

class LocalPacketString(BigEndianStructure):
    _fields_ = [("length", c_ushort),
                ("value", c_char * length)]
ps = LocalPacketString(length, buff)

但是,您可能会注意到,这并不能为您节省多少工作而只是保持类型分开。


总之,struct hack甚至不是有效的C,并且它不能很好地映射到ctypesctypes.Structure不是表示可变长度前缀字符串的好方法。