,如同在Python 3的大多數事情,故意不自動Unicode和字節之間的轉換。這是因爲在大多數使用情況下,這隻會要求與人們切換到Python 3的相同類型的Mojibake或UnicodeEncodeError
災難。
但是,當你知道你只處理純ASCII時,那是另一回事。你必須是明確的 - 但你可以將這種明確性分解爲包裝。
如Specifying the required argument types (function prototypes)解釋的那樣,除了標準的類型,則可以通過具有from_param
類方法 - 這通常與_as_parameter_
屬性返回一些類型的實例(通常是相同的類型)的任何類,但也可以返回原生的-類型值。
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
......但你不會想隱式地將int
轉換爲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
以同樣的方式。
如果你也想自動解碼的返回類型,見Return types和errcheck
。
最後一件事:當你確定的數據是應該是UTF-8,但你想要對付他們不是以同樣的方式的Python 2.x的情況下將(通過保持原樣),你甚至可以在3.x中做到這一點。使用前面提到的Utf8ifier
作爲您的argtype,並使用解碼器errcheck,並使用errors=surrogateescape
。完整的例子見here。
@eryksun:我通常在示例中顯示'LoadLibrary'調用,因爲這是文檔中第一個非Windows示例,我不想解釋不相關的東西。但現在我想到了,這很愚蠢,特別是下一行顯示的是更簡單的例子。謝謝! – abarnert
我的確喜歡這種方法,但我希望有一種不需要每個功能代碼的解決方案。這比在每個函數定義中插入 'value = value.encode('ascii')'更優雅,但我仍然想知道是否可以通過改變ctypes定義本身來做得更好?而不是 'ViString = _ctypes.c_char_p' 像 'ViString = _ctypes.my_type' 其中my_type從c_char_p繼承,但首先編碼爲ASCII? –
@MatthewLawson:我不確定我明白你在問什麼。什麼是'ViString'?它似乎只是'c_char_p'類型的另一個名稱,所以...你如何使用它?更重要的是:您應該爲通過ctypes使用的每個C函數設置''argtypes''(否則,事情將很難發揮作用,當沒有太多參數時,它們的大小將與int ,你很幸運......這通常不夠好)。那麼,如何將'Asciifier'或其他什麼比'ctypes.c_char_p'更難? – abarnert