在Python ctypes中转换为数组

3 投票
3 回答
4664 浏览
提问于 2025-04-16 01:33

我正在尝试把通过socket.inet_pton得到的16字节数据转换成一个无符号字节的ctypes数组。我的数据结构是这样的:

class in6_addr(ctypes.Structure):
    _fields_ = (("Byte", ctypes.c_ubyte * 16),)

而这个数据块就是:

data = socket.inet_pton(socket.AF_INET6, "2001::3")

不过,我的这些尝试都出现了错误:

sin6 = in6_addr()

# TypeError: expected c_ubyte_Array_16 instance, got str
sin6.Byte = data
# TypeError: cast() argument 2 must be a pointer type, not c_ubyte_Array_16
sin6.Byte = ctypes.cast(data, ctypes.c_ubyte * 16)
# TypeError: incompatible types, LP_c_ubyte instance instead of c_ubyte_Array_16 instance
sin6.Byte = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte))

所有的代码可以在这里查看:http://codepad.org/2cjyVXBA

有没有人知道我需要转换成什么类型呢?

3 个回答

0

最简单的方法:

import ctypes
import socket

class in6_addr(ctypes.Structure):
    _fields_ = (("Byte", ctypes.c_ubyte * 16),)

data = socket.inet_pton(socket.AF_INET6, "2001::3")

sin6 = in6_addr()
sin6.Byte[:] = data   # copy the data into a complete slice of the array

还有几个辅助方法可以让这个类更容易使用:

import ctypes
import socket

class in6_addr(ctypes.Structure):
    _fields_ = ("Byte", ctypes.c_ubyte * 16),

    def __init__(self, data):
        self.Byte[:] = data

    def __repr__(self):
        return f'in6_addr(Bytes=bytes({list(self.Byte)}))'

data = socket.inet_pton(socket.AF_INET6, "2001::3")
sin6 = in6_addr(data)
print(sin6)

输出结果:

in6_addr(Bytes=bytes([32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]))
2

可以说更简单:

sin6.Byte = cast(data, ctypes.POINTER(ctypes.c_ubyte * 16)).contents

或者:

sin6.Byte = (ctypes.c_ubyte * 16)(*[x for x in data])

使用字节流:

import io
io.BytesIO(data).readinto(sin6.Byte)

因为这个结构只有一个字段,所以字段名可以省略:

sin6 = (ctypes.c_ubyte * 16)(*data)
sin6 = cast(data, ctypes.POINTER(ctypes.c_ubyte * 16)).contents
io.BytesIO(data).readinto(sin6)
3

我可能完全错了(这看起来有点复杂),但对我来说这样做是有效的:

sin6.Byte = (ctypes.c_ubyte*16)(*list(bytearray(data)))

我需要把数据转换成一个整数列表,然后再把它们拆开给构造函数用。肯定有更简单的方法!

撰写回答