将VBA类型/C结构移植到Python ctypes。结构:具有固定长度的字符串数组

将VBA类型/C结构移植到Python ctypes。结构:具有固定长度的字符串数组,python,c,vba,struct,ctypes,Python,C,Vba,Struct,Ctypes,我正在尝试将一段VBA代码移植到Python。这项工作包括在Windows DLL中调用函数。该函数需要一个指向C结构(在VBA中称为“类型”)的指针作为参数。上述结构包含固定长度的字符串以及固定长度的字符串数组。我正在努力寻找一种使用ctypes在Python中表达这一点的方法 原始VBA代码包含如下语句: Public Type elements elementA As String * 48 elementB(3) As String * 12 End Type 我认为这

我正在尝试将一段VBA代码移植到Python。这项工作包括在Windows DLL中调用函数。该函数需要一个指向C结构(在VBA中称为“类型”)的指针作为参数。上述结构包含固定长度的字符串以及固定长度的字符串数组。我正在努力寻找一种使用ctypes在Python中表达这一点的方法

原始VBA代码包含如下语句:

Public Type elements
    elementA As String * 48
    elementB(3) As String * 12
End Type
我认为这可以用C来表示:

struct elements
{
    char elementA[48];
    char elementB[4][12];
}
到目前为止,我在Python中尝试了以下内容:

import ctypes

class elements(ctypes.Structure):
    _fields_ = [
        ("elementA", ctypes.create_string_buffer(48)), 
        ("elementB", ctypes.create_string_buffer(12) * 4)
        ]
我可以成功地声明elementA,尽管声明elementB失败了

“TypeError:不支持*:'c_char_Array_12'和'int'的操作数类型”

如何以正确的方式做到这一点


更新#1

我可以成功声明以下内容:

import ctypes

class elements(ctypes.Structure):
    _fields_ = [
        ("elementA", ctypes.c_char * 48), 
        ("elementB", ctypes.c_char * 12 * 4)
        ]
elementA公开了一个“value”属性,而我找不到使用elementB的方法。如何阅读或更改其内容


更新#2

我想我理解这种行为

>>> e = elements()
>>> e.elementA
''
>>> e.elementA = 'test'
>>> e.elementA
'test'
>>> e.elementB
<__main__.c_char_Array_12_Array_4 object at 0x9878ecc>
>>> e.elementB[0][:] == '\x00' * 12
True
>>> e.elementB[0][:]
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> e.elementB[0][:] = 'test'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Can only assign sequence of same size
>>> e.elementB[0][:] = 'test' + '\x00' * 8
>>> e.elementB[0][:]
'test\x00\x00\x00\x00\x00\x00\x00\x00'
>>> testB = 'abcde'
>>> e.elementB[0][:] = testB + '\x00' * ( ctypes.sizeof(e.elementB[0]) - len(testB) )
>>> e.elementB[0][:]
'abcde\x00\x00\x00\x00\x00\x00\x00'
>>> e.elementB[0][:].rstrip('\x00')
'abcde'
>>> e.elementB[0].value
'abcde'
>>> e.elementB[0].value = 'abcdef'
>>> e.elementB[0][:]
'abcdef\x00\x00\x00\x00\x00\x00'
>e=elements()
>>>元素e.elementA
''
>>>e.elementA=‘测试’
>>>元素e.elementA
“测试”
>>>e.elementB
>>>e.elementB[0][:]='\x00'*12
真的
>>>e.elementB[0][:]
“\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00”
>>>e.elementB[0][:]=“测试”
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
ValueError:只能分配相同大小的序列
>>>e.elementB[0][:]='test'+'\x00'*8
>>>e.elementB[0][:]
'测试\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>>testB='abcde'
>>>e.elementB[0][:]=testB+'\x00'*(ctypes.sizeof(e.elementB[0])-len(testB))
>>>e.elementB[0][:]
“abcde\x00\x00\x00\x00\x00\x00\x00\x00”
>>>e.elementB[0][:].rstrip('\x00')
“abcde”
>>>e.elementB[0]。值
“abcde”
>>>e.elementB[0]。值='abcdef'
>>>e.elementB[0][:]
“abcdef\x00\x00\x00\x00\x00\x00\x00”

(此问题涉及Python 2.6和2.7。)

创建字符串缓冲区
是创建
c\u char
数组实例的方便函数。但是,字段定义需要C类型,而不是实例。例如:

import ctypes

class elements(ctypes.Structure):
    _fields_ = [("elementA", ctypes.c_char * 48), 
                ("elementB", ctypes.c_char * 12 * 4)]
假设您有一个定义如下的C函数:

lib.func.argtypes = [ctypes.POINTER(elements)]
要调用此函数,请使用
byref
传递
元素的实例:

e = elements()
lib.func(ctypes.byref(e))
访问一维
c_char
数组字段(如
elementA
)是返回Python字符串的特殊情况。但是访问二维数组,例如
elementB
,会返回一个ctypes
array
实例。对于
elementB
有4行,每行包含12列

>>> len(e.elementB)
4
>>> map(len, e.elementB)
[12, 12, 12, 12]
sizeof
返回数组的大小(以字节为单位)。例如,
elementB
的缓冲区由48个
c_char
元素组成,每个元素有1个字节:

>>> ctypes.sizeof(e.elementB)
48
elementB
c\u char
数组作为字符数组,其大小写特殊,具有
原始
属性。获取
属性将创建一个Python字符串,该字符串将数组视为以null结尾的C字符串。
raw
属性返回整个长度。您还可以使用这些属性分配Python字符串,并且两者都接受包含null的字符串

>>> e.elementB[3].value = 'abc\x00def'
>>> e.elementB[3].value
'abc'
>>> e.elementB[3].raw
'abc\x00def\x00\x00\x00\x00\x00'
或切片数组以获取子字符串:

>>> e.elementB[3][:]
'abc\x00def\x00\x00\x00\x00\x00'
>>> e.elementB[3][4:7]
'def'
c_wchar
数组只有
value
属性,该属性返回一个
unicode
字符串。您可以使用
unicode
字符串或(在python2中)8位字符串设置
value
。使用当前的ctypes编码对8位字符串进行解码,在Windows上默认为
'mbcs'
,否则默认为
'ascii'
<代码>设置转换模式(Python 2)设置默认编码:

>>> s = (ctypes.c_wchar * 12)()
>>> v = u'\u0100'.encode('utf-8')
>>> v
'\xc4\x80'
>>> s.value = v
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0: 
ordinal not in range(128)
转换编码设置为UTF-8后,分配
'\xc4\x80'
即可工作:

>>> s.value = v
>>> s.value
u'\u0100'
>>> s[:]
u'\u0100\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
数组是可编辑的:

for row in e.elementB:
    row[:] = 'abcdefghijkl'

>>> print '\n'.join(row[::-1] for row in e.elementB)
lkjihgfedcba
lkjihgfedcba
lkjihgfedcba
lkjihgfedcba
ctypes数据类型支持Python的缓冲区协议,以便与其他类型进行交互操作:

>>> bytearray(e.elementB)
bytearray(b'abcdefghijklabcdefghijklabcdefghijklabcdefghijkl')

>>> import numpy as np
>>> np.frombuffer(e.elementB, dtype='uint8')
array([ 97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108,  97,
        98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108,  97,  98,
        99, 100, 101, 102, 103, 104, 105, 106, 107, 108,  97,  98,  99,
       100, 101, 102, 103, 104, 105, 106, 107, 108], dtype=uint8)

@谢谢你的帮助。我在我的问题中加入了你的评论,但我很难使用它。2D char数组不公开任何类似于“value”属性的内容,我不知道如何读取或写入此数组。@eryksun再次感谢。我想我现在了解了这种行为——请参阅我问题的最后更新。你想把这个问题总结成一个答案吗?
>>> bytearray(e.elementB)
bytearray(b'abcdefghijklabcdefghijklabcdefghijklabcdefghijkl')

>>> import numpy as np
>>> np.frombuffer(e.elementB, dtype='uint8')
array([ 97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108,  97,
        98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108,  97,  98,
        99, 100, 101, 102, 103, 104, 105, 106, 107, 108,  97,  98,  99,
       100, 101, 102, 103, 104, 105, 106, 107, 108], dtype=uint8)