BigEndianStructure中的字符串 [英] Strings in BigEndianStructure

查看:110
本文介绍了BigEndianStructure中的字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做这样的事情:

  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。脚本根本不会运行。这是错误的消息:

 跟踪(最近一次通话最近):
文件 C:\PKOEmu\test .py,第8行,在< module>中
类PacketString(BigEndianStructure):
文件 C:\Python27\lib\ctypes\_endian.py,第34行,位于__setattr__
fields.append(( _other_endian(typ))+ rest)
文件 C:\Python27\lib\ctypes\_endian.py,第24行,在_other_endian
中引发TypeError(其他字节序:%s%typ)
TypeError:此类型不支持其他字节序:< class'ctypes.c_char_p'>


解决方案

此类型:

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

…甚至忽略了字节序问题,也没有做您认为的事情。这是一个包含ushort长度的结构,然后是指向内存中其他地方的实际字符串数据的指针。



换句话说,它就像下面的C结构:

  struct PacketString {
无符号短长度;
char *值;
};

您要查找的是一个长度前缀的字符串,该字符串直接插入结构。为此,C结构为:

  struct PacketString {
无符号短长度;
个字符值[1];
};

这称为结构破解。这实际上不是合法的C语言,但是它恰好适用于每个已知的C89编译器以及大多数C99和C ++编译器。有关详细信息,请参见 C常见问题解答条目



那么,您可以在 ctypes 中做同样的事情吗?好吧,是的,但是它没有那么有用:

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

变得复杂请参阅文档中的变量大小的数据类型有关详细信息。特别是,您不能在 p.value 上调用调整大小;您需要计算自己调整 p 大小的大小,然后将 p._fields_ [1] 的类型更改为



嗯,这就是为什么文档说:


将可变大小的数据类型与ctypes一起使用的另一种方法是使用Python的动态特性,并根据具体情况在所需的大小已知后重新定义数据类型。 / p>

换句话说:

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

但是,您可能会注意到,这与单独保存类型相比并没有节省很多工作。




总而言之,struct hack甚至都不是有效的C,它与<$ c $的映射也不太好c> ctypes 。 ctypes.Structure 并不是表示长度可变的字符串的好方法。


I want to do something like that:

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)]

However I getting error because c_char_p can only be in native byte order. But maybe there is some other way how I can make strings whose length is specified right before them. I just like how structs are easy to read from/write to socket. And how you define just _fields_ and then can use it like that:

initialPacket = InitialPacket()
initialPacket.command = 128

The question is: How do I create variable-length field in BigEndianStructure? Because Python wouldn't allow me to use c_char_p. Script simply wont run at all. This is error:

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'>

解决方案

This type:

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

… doesn't do what you think it does, even ignoring the endianness issue. It's a struct that contains a ushort length, and then a pointer to the actual string data somewhere else in memory.

In other words, it's just like this C structure:

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

What you're looking for is a length-prefixed string, where the string is directly inline inside the struct. For that, the C structure is:

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

This is called the "struct hack". This is not actually legal C, but it happens to work with every known C89 compiler, and most C99 and C++ compilers. See the C FAQ entry fro details.

So, can you do the same thing in ctypes? Well, yes, but it's not as useful:

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

This can get complicated; see Variable-sized data types in the docs for details. In particular, you can't call resize on p.value; you need to calculate how much to resize p itself, and then change the type of p._fields_[1] to the have the right type, and then…

Well, this is why the docs say:

Another way to use variable-sized data types with ctypes is to use the dynamic nature of Python, and (re-)define the data type after the required size is already known, on a case by case basis.

In other words:

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

However, you may notice that this isn't really saving you much work over just keeping the types separate.


In summary, the struct hack isn't even valid C, and it doesn't map very well to ctypes. A ctypes.Structure is not a good way to represent a variable-length length-prefixed string.

这篇关于BigEndianStructure中的字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆