ここの例に従って、バイナリ形式を処理しようとしています:
http://dabeaz.blogspot.jp/2009/08/python-binary-io-handling.html
>>> from ctypes import *
>>> class Point(Structure):
>>> _fields_ = [ ('x',c_double), ('y',c_double), ('z',c_double) ]
>>>
>>> g = open("foo","rb") # point structure data
>>> q = Point()
>>> g.readinto(q)
24
>>> q.x
2.0
ヘッダーの構造を定義し、構造にデータを読み込もうとしていますが、問題があります。私の構造は次のようなものです:
class BinaryHeader(BigEndianStructure):
_fields_ = [
("sequence_number_4bytes", c_uint),
("ascii_text_32bytes", c_char),
("timestamp_4bytes", c_uint),
("more_funky_numbers_7bytes", c_uint, 56),
("some_flags_1byte", c_byte),
("other_flags_1byte", c_byte),
("payload_length_2bytes", c_ushort),
]
ctypes のドキュメントには次のように書かれています。
c_int のような整数型フィールドの場合、3 番目のオプション項目を指定できます。フィールドのビット幅を定義する小さな正の整数でなければなりません。
そのため("more_funky_numbers_7bytes", c_uint, 56),
、フィールドを 7 バイト フィールドとして定義しようとしましたが、次のエラーが発生します。
ValueError: ビット フィールドの無効なビット数
私の最初の問題は、どうすれば 7 バイトの int フィールドを定義できるかということです。
次に、その問題をスキップして「more_funky_numbers_7bytes」フィールドをコメントアウトすると、結果のデータが読み込まれます..しかし、予想どおり、「ascii_text_32bytes」には1文字しか読み込まれません。そして、何らかの理由16
で、構造体に読み込まれた計算されたバイト数であると想定する戻り値が返されます...しかし、「ファンキーな数値」フィールドをコメントアウトしていて、「ascii_text_32bytes」が1文字(1バイト)しか与えていない場合、 16じゃなくて13じゃないの???
次に、char フィールドを別の構造に分割して、それをヘッダー構造内から参照しようとしました。しかし、それもうまくいきません...
class StupidStaticCharField(BigEndianStructure):
_fields_ = [
("ascii_text_1", c_byte),
("ascii_text_2", c_byte),
("ascii_text_3", c_byte),
("ascii_text_4", c_byte),
("ascii_text_5", c_byte),
("ascii_text_6", c_byte),
("ascii_text_7", c_byte),
("ascii_text_8", c_byte),
("ascii_text_9", c_byte),
("ascii_text_10", c_byte),
("ascii_text_11", c_byte),
.
.
.
]
class BinaryHeader(BigEndianStructure):
_fields_ = [
("sequence_number_4bytes", c_uint),
("ascii_text_32bytes", StupidStaticCharField),
("timestamp_4bytes", c_uint),
#("more_funky_numbers_7bytes", c_uint, 56),
("some_flags_1byte", c_ushort),
("other_flags_1byte", c_ushort),
("payload_length_2bytes", c_ushort),
]
したがって、次の方法についてのアイデアは次のとおりです。
- 7 バイト フィールドを定義します (定義済みの関数を使用してデコードする必要があります)。
- 32 バイトの static char フィールドを定義する
アップデート
機能しそうな構造を見つけました...
class BinaryHeader(BigEndianStructure):
_fields_ = [
("sequence_number_4bytes", c_uint),
("ascii_text_32bytes", c_char * 32),
("timestamp_4bytes", c_uint),
("more_funky_numbers_7bytes", c_byte * 7),
("some_flags_1byte", c_byte),
("other_flags_1byte", c_byte),
("payload_length_2bytes", c_ushort),
]
しかし、私の残りの質問は、なぜ使用するのかということ.readinto()
です:
f = open(binaryfile, "rb")
mystruct = BinaryHeader()
f.readinto(mystruct)
それは戻ってき52
て、期待されたものではありません51
. その余分なバイトはどこから来て、どこへ行くのでしょうか?
更新 2
興味のある方のために、eryksun が言及した名前付きタプルに値を読み込む代替方法の例を次に示します。struct
>>> record = 'raymond \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record)
>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name='raymond ', serialnum=4658, school=264, gradelevel=8)