ctypes
,像 Python 3 中的大多数东西一样,故意不会在 unicode 和字节之间自动转换。那是因为在大多数用例中,这只是要求UnicodeEncodeError
人们切换到 Python 3 以避免相同类型的 mojibake 或灾难。
但是,当您知道您只处理纯 ASCII 时,那就是另一回事了。您必须是明确的——但您可以将这种明确性分解到包装器中。
正如指定所需的参数类型(函数原型)中所解释的,除了标准ctypes
类型之外,您还可以传递任何具有类方法的from_param
类——它通常返回具有_as_parameter_
属性的某种类型(通常是相同类型)的实例,但可以也只是返回一个 nativectypes
类型的值。
class Asciifier(object):
@classmethod
def from_param(cls, value):
if isinstance(value, bytes):
return value
else:
return value.encode('ascii')
这可能不是您想要的确切规则 - 例如,它会失败bytearray
(就像c_char_p
会一样),即使它可以悄悄地转换为bytes
... 但你不想隐式转换 an int
to bytes
。任何东西,无论你决定什么规则都应该很容易编码。
这是一个示例(在 OS X 上;您显然必须更改libc
linux、Windows 等的加载方式,但您大概知道如何做到这一点):
>>> libc = CDLL('libSystem.dylib')
>>> libc.atoi.argtypes = [Asciifier]
>>> libc.atoi.restype = c_int
>>> libc.atoi(b'123')
123
>>> libc.atoi('123')
123
>>> libc.atoi('123') # Unicode fullwidth digits
ArgumentError: argument 1: <class 'UnicodeEncodeError'>: 'ascii' codec can't encode character '\uff10' in position 0: ordinal not in range(128)
>>> libc.atoi(123)
ArgumentError: argument 1: <class 'AttributeError'>: 'int' object has no attribute 'encode'
显然,如果这些对您的用例不够清楚,您可以捕获异常并引发不同的异常。
您可以类似地为某个特定库编写一个Utf8ifier
、 或一个Encodifier(encoding, errors=None)
类工厂,或任何其他您需要的东西,并argtypes
以相同的方式粘贴它。
如果您还想自动解码返回类型,请参阅返回类型和errcheck
.
最后一件事:当您确定数据应该是 UTF-8 时,但您想处理它们与 Python 2.x 不同的情况(通过按原样保留它们),你甚至可以在 3.x 中做到这一点。使用上述Utf8ifier
作为您的 argtype 和解码器 errcheck,然后使用errors=surrogateescape
. 有关完整示例,请参见此处。