0

私はそのようなことをしたい:

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

1 に答える 1

5

このタイプ:

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];
};

これは「構造体ハック」と呼ばれます。これは実際には合法的な C ではありませんが、既知のすべての C89 コンパイラ、およびほとんどの C99 および C++ コンパイラで機能します。詳細については、C FAQ エントリを参照してください。

それで、あなたは同じことをすることができますctypesか?はい、しかし、それほど役に立ちません:

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

これは複雑になる可能性があります。詳細については、ドキュメントの可変サイズのデータ​​型を参照してください。resize特に、を呼び出すことはできませんp.valuepそれ自体のサイズをどれだけ変更するかを計算し、 のタイプをp._fields_[1]適切なタイプに変更する必要があります。

さて、これがドキュメントが言う理由です:

ctypes で可変サイズのデータ​​型を使用するもう 1 つの方法は、Python の動的な性質を使用し、必要なサイズが既にわかっている場合に、データ型を (再) 定義することです。

言い換えると:

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

ただし、これは単に型を分離しておくよりも多くの作業を節約できるわけではないことに気付くかもしれません。


要約すると、構造体ハックは有効な C でさえありませんctypes。Actypes.Structureは、長さがプレフィックス付きの可変長文字列を表すのに適した方法ではありません。

于 2013-07-09T22:49:24.420 に答える