gpt4 book ai didi

python - 如何在 python ctypes 中使用 UTF-16?

转载 作者:行者123 更新时间:2023-11-28 22:38:33 26 4
gpt4 key购买 nike

我有一个在 API 中使用 utf-16 的外国 C 库:作为函数参数、返回值和结构成员。

在 Windows 上,它可以使用 ctypes.c_wchar_p,但在 OSX 下,ctypes 在 c_wchar 中使用 UCS-32,我找不到支持 utf-16 的方法。

这是我的研究:

  1. 使用 _SimpleCData 子类化 redefine _check_retval_ .

    • 它允许将 utf-16 透明地转换为 Python 字符串。
    • 可以作为C结构成员放置
    • 但它不允许将字符串作为参数处理,它的 from_param() 方法从未被调用(为什么?):func('str', b'W\x00B\x00\x00\x00') # 没有转换就通过了
  2. 通过from_param() 方法使用自己的类型。

    • 优点:可以使用构造函数初始化,也可以在将字符串传递给函数时动态编码:
    • 缺点:不能作为函数返回类型或结构成员。

这里是:

ustr = myutf16('hello')
func(ustr)
func('hello') # calls myutf16.from_param('hello')

最佳答案

您可以覆盖 c_char_p 子类中的 from_param 以将 unicode 字符串编码为 UTF-16。您可以添加 _check_retval_ 方法将 UTF-16 结果解码为 unicode 字符串。对于结构字段,您可以使用处理设置和获取属性的描述符类。使字段成为 c_char_p 类型的私有(private) _name,并将描述符设置为公共(public) name。例如:

import sys
import ctypes

if sys.version_info[0] > 2:
unicode = str

def decode_utf16_from_address(address, byteorder='little',
c_char=ctypes.c_char):
if not address:
return None
if byteorder not in ('little', 'big'):
raise ValueError("byteorder must be either 'little' or 'big'")
chars = []
while True:
c1 = c_char.from_address(address).value
c2 = c_char.from_address(address + 1).value
if c1 == b'\x00' and c2 == b'\x00':
break
chars += [c1, c2]
address += 2
if byteorder == 'little':
return b''.join(chars).decode('utf-16le')
return b''.join(chars).decode('utf-16be')

class c_utf16le_p(ctypes.c_char_p):
def __init__(self, value=None):
super(c_utf16le_p, self).__init__()
if value is not None:
self.value = value

@property
def value(self,
c_void_p=ctypes.c_void_p):
addr = c_void_p.from_buffer(self).value
return decode_utf16_from_address(addr, 'little')

@value.setter
def value(self, value,
c_char_p=ctypes.c_char_p):
value = value.encode('utf-16le') + b'\x00'
c_char_p.value.__set__(self, value)

@classmethod
def from_param(cls, obj):
if isinstance(obj, unicode):
obj = obj.encode('utf-16le') + b'\x00'
return super(c_utf16le_p, cls).from_param(obj)

@classmethod
def _check_retval_(cls, result):
return result.value

class UTF16LEField(object):
def __init__(self, name):
self.name = name

def __get__(self, obj, cls,
c_void_p=ctypes.c_void_p,
addressof=ctypes.addressof):
field_addr = addressof(obj) + getattr(cls, self.name).offset
addr = c_void_p.from_address(field_addr).value
return decode_utf16_from_address(addr, 'little')

def __set__(self, obj, value):
value = value.encode('utf-16le') + b'\x00'
setattr(obj, self.name, value)

示例:

if __name__ == '__main__':
class Test(ctypes.Structure):
_fields_ = (('x', ctypes.c_int),
('y', ctypes.c_void_p),
('_string', ctypes.c_char_p))
string = UTF16LEField('_string')

print('test 1: structure field')
t = Test()
t.string = u'eggs and spam'
print(t.string)

print('test 2: parameter and result')
result = None

@ctypes.CFUNCTYPE(c_utf16le_p, c_utf16le_p)
def testfun(string):
global result
print('parameter: %s' % string.value)
# callbacks leak memory except for simple return
# values such as an integer address, so return the
# address of a global variable.
result = c_utf16le_p(string.value + u' and eggs')
return ctypes.c_void_p.from_buffer(result).value

print('result: %s' % testfun(u'spam'))

输出:

test 1: structure field
eggs and spam

test 2: parameter and result
parameter: spam
result: spam and eggs

关于python - 如何在 python ctypes 中使用 UTF-16?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35500018/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com