4

昨晚我受够了,开始将 PyVISA 移植到Python 3(此处的进度:https ://github.com/thevorpalblade/pyvisa )。

只要我将设备地址(实际上是任何字符串)作为ASCII字符串而不是默认的 unicode 字符串(例如,
HP = vida.instrument(b"GPIB: :16") 有效,而 HP = vida.instrument("GPIB::16") 无效,引发 ValueError。

理想情况下,最终用户不必关心字符串编码。关于我应该如何处理这个问题的任何建议?ctypes 类型定义中的某些内容可能是什么?

就目前而言,相关的 ctypes 类型定义是:

ViString = _ctypes.c_char_p
4

1 回答 1

6

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 intto bytes。任何东西,无论你决定什么规则都应该很容易编码。


这是一个示例(在 OS X 上;您显然必须更改libclinux、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. 有关完整示例,请参见此处

于 2014-01-22T23:25:36.447 回答