Python 带有结构数组的 ctype 结构
Python ctype struct with array of structs
我有一些结构,具有动态条目数。我从 UDP 接收 bytearray 并按如下方式解析此消息:
class MsgStruct(Structure):
_pack_ = 1
def __init__(self, data=None):
if data:
self.unpack(data)
def unpack(self, raw):
fit = sizeof(self)
memmove(addressof(self), raw[:fit], fit)
def pack(self):
return bytearray(self)[:]
def size(self):
return sizeof(self)
class MessageEntry(MsgStruct):
_fields_ = [
('type', c_byte),
('flag', c_byte),
('count', c_int)]
class Message(MsgStruct):
_fields_ = [
('id', c_int),
('entry_count', c_int)]
entries = []
def __init__(self, data=None):
MsgStruct.__init__(self, data=data)
if data:
self.parseEntries(data[self.entry_count:])
def parseEntries(self, data):
offset = 0
size = sizeof(MessageEntry())
for count in range(self.entry_count):
entry = MessageEntry(data[offset:offset+size])
self.entries.append(entry)
offset += size
但我认为有更好的方法可以使用 ctypes.Array 或 POINTER 解析消息并尝试这样做:
class Message(MsgStruct):
_fields_ = [
('id', c_int),
('entry_count', c_int),
('entries', POINTER(MessageEntry))]
def __init__(self, data=None):
MsgStruct.__init__(self, data=data)
if data:
self.parseEntries(data[self.entry_count:])
def parseEntries(self, data):
offset = 0
size = sizeof(MessageEntry())
elems = (MessageEntry * self.entry_count)()
self.entries = cast(elems, POINTER(MessageEntry))
for count in range(self.entry_count):
self.entries[count] = MessageEntry(data[offset:offset+size])
offset += size
但是当我尝试打印条目时,我陷入了无限循环
msg = Message(x)
for i in msg.entries:
print(i)
我做错了什么?
是否有另一种方法来解析带有动态条目的消息?
我想首先说明一下,我没有看到 entry_count 属性在哪里初始化。
将指针当作 大小的 数组进行迭代在概念上是错误的(正如 [Python 3]: ctypes - A foreign function library for Python 中所指出的)。在 C 中,可以超出数组边界,但 ctypes 禁止这样做。
这里有一个更简单的例子,它使用 ctypes.c_char
作为基本类型(MessageEntry 对应)。
code.py:
#!/usr/bin/env python3
import sys
import ctypes
def main():
CharArr5 = ctypes.c_char * 5
b5 = b"12345"
ca5 = CharArr5(*b5)
print("Print array ...")
for c in ca5:
print(c)
cp = ctypes.cast(ca5, ctypes.POINTER(ctypes.c_char))
max_values = 10
print("\nPrint pointer (max {:d} values) ...".format(max_values))
for idx, c in enumerate(cp):
print(c)
if idx >= max_values:
print("Max value number reached.")
break
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
输出:
(py_064_03.06.08_test0) e:\Work\Dev\Whosebug\q054178876>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32
Print array ...
b'1'
b'2'
b'3'
b'4'
b'5'
Print pointer (max 10 values) ...
b'1'
b'2'
b'3'
b'4'
b'5'
b'\x00'
b'\x00'
b'\x00'
b'\x00'
b'\x00'
b'\x00'
Max value number reached.
如上所示, 可以遍历指针,但循环永远不会结束(当到达不可用/无效的内存地址时它将结束,并且程序将 segfault(访问冲突))。
假设entry_count正确初始化(如果没有,请确保初始化它),用它来保持边界内的循环(如下所示):
for idx in range(msg.entry_count):
msg.entries[idx] # Do smth with it
# ...
for idx, entry in enumerate(msg.entries):
if idx >= msg.entry_count:
break
entry # Do smth with it
# ...
或者您可以使用上述方法之一为 Message.
实现 Iterator Protocol
我有一些结构,具有动态条目数。我从 UDP 接收 bytearray 并按如下方式解析此消息:
class MsgStruct(Structure):
_pack_ = 1
def __init__(self, data=None):
if data:
self.unpack(data)
def unpack(self, raw):
fit = sizeof(self)
memmove(addressof(self), raw[:fit], fit)
def pack(self):
return bytearray(self)[:]
def size(self):
return sizeof(self)
class MessageEntry(MsgStruct):
_fields_ = [
('type', c_byte),
('flag', c_byte),
('count', c_int)]
class Message(MsgStruct):
_fields_ = [
('id', c_int),
('entry_count', c_int)]
entries = []
def __init__(self, data=None):
MsgStruct.__init__(self, data=data)
if data:
self.parseEntries(data[self.entry_count:])
def parseEntries(self, data):
offset = 0
size = sizeof(MessageEntry())
for count in range(self.entry_count):
entry = MessageEntry(data[offset:offset+size])
self.entries.append(entry)
offset += size
但我认为有更好的方法可以使用 ctypes.Array 或 POINTER 解析消息并尝试这样做:
class Message(MsgStruct):
_fields_ = [
('id', c_int),
('entry_count', c_int),
('entries', POINTER(MessageEntry))]
def __init__(self, data=None):
MsgStruct.__init__(self, data=data)
if data:
self.parseEntries(data[self.entry_count:])
def parseEntries(self, data):
offset = 0
size = sizeof(MessageEntry())
elems = (MessageEntry * self.entry_count)()
self.entries = cast(elems, POINTER(MessageEntry))
for count in range(self.entry_count):
self.entries[count] = MessageEntry(data[offset:offset+size])
offset += size
但是当我尝试打印条目时,我陷入了无限循环
msg = Message(x)
for i in msg.entries:
print(i)
我做错了什么? 是否有另一种方法来解析带有动态条目的消息?
我想首先说明一下,我没有看到 entry_count 属性在哪里初始化。
将指针当作 大小的 数组进行迭代在概念上是错误的(正如 [Python 3]: ctypes - A foreign function library for Python 中所指出的)。在 C 中,可以超出数组边界,但 ctypes 禁止这样做。
这里有一个更简单的例子,它使用 ctypes.c_char
作为基本类型(MessageEntry 对应)。
code.py:
#!/usr/bin/env python3
import sys
import ctypes
def main():
CharArr5 = ctypes.c_char * 5
b5 = b"12345"
ca5 = CharArr5(*b5)
print("Print array ...")
for c in ca5:
print(c)
cp = ctypes.cast(ca5, ctypes.POINTER(ctypes.c_char))
max_values = 10
print("\nPrint pointer (max {:d} values) ...".format(max_values))
for idx, c in enumerate(cp):
print(c)
if idx >= max_values:
print("Max value number reached.")
break
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
输出:
(py_064_03.06.08_test0) e:\Work\Dev\Whosebug\q054178876>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32 Print array ... b'1' b'2' b'3' b'4' b'5' Print pointer (max 10 values) ... b'1' b'2' b'3' b'4' b'5' b'\x00' b'\x00' b'\x00' b'\x00' b'\x00' b'\x00' Max value number reached.
如上所示, 可以遍历指针,但循环永远不会结束(当到达不可用/无效的内存地址时它将结束,并且程序将 segfault(访问冲突))。
假设entry_count正确初始化(如果没有,请确保初始化它),用它来保持边界内的循环(如下所示):
for idx in range(msg.entry_count):
msg.entries[idx] # Do smth with it
# ...
for idx, entry in enumerate(msg.entries):
if idx >= msg.entry_count:
break
entry # Do smth with it
# ...
或者您可以使用上述方法之一为 Message.
实现 Iterator Protocol