2014-01-22 58 views
2

昨晚我受夠了並開始將PyVISA移植到Python 3(此處的進度:https://github.com/thevorpalblade/pyvisa)。將Python 2代碼移植到Python時處理ctypes和ASCII字符串3

我已經得到了它的地方一切正常,只要我通過點的設備地址(當然,任何字符串真的)作爲ASCII字符串,而不是默認的Unicode字符串(例如,
HP = VIDA。儀器(b「GPIB :: 16」)工作,而 HP = vida.instrument(「GPIB :: 16」)不會引起ValueError。

理想情況下,最終用戶不必關心字符串編碼 關於我該如何處理這個問題的任何建議?ctypes類型定義中的某些東西可能是?

因爲它,螞蟻ctypes的類型定義爲:

ViString = _ctypes.c_char_p 

回答

4

​​,如同在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 typeserrcheck


最後一件事:當你確定的數據是應該是UTF-8,但你想要對付他們不是以同樣的方式的Python 2.x的情況下將(通過保持原樣),你甚至可以在3.x中做到這一點。使用前面提到的Utf8ifier作爲您的argtype,並使用解碼器errcheck,並使用errors=surrogateescape。完整的例子見here

+0

@eryksun:我通常在示例中顯示'LoadLibrary'調用,因爲這是文檔中第一個非Windows示例,我不想解釋不相關的東西。但現在我想到了,這很愚蠢,特別是下一行顯示的是更簡單的例子。謝謝! – abarnert

+0

我的確喜歡這種方法,但我希望有一種不需要每個功能代碼的解決方案。這比在每個函數定義中插入 'value = value.encode('ascii')'更優雅,但我仍然想知道是否可以通過改變ctypes定義本身來做得更好?而不是 'ViString = _ctypes.c_char_p' 像 'ViString = _ctypes.my_type' 其中my_type從c_char_p繼承,但首先編碼爲ASCII? –

+0

@MatthewLawson:我不確定我明白你在問什麼。什麼是'ViString'?它似乎只是'c_char_p'類型的另一個名稱,所以...你如何使用它?更重要的是:您應該爲通過ctypes使用的每個C函數設置''argtypes''(否則,事情將很難發揮作用,當沒有太多參數時,它們的大小將與int ,你很幸運......這通常不夠好)。那麼,如何將'Asciifier'或其他什麼比'ctypes.c_char_p'更難? – abarnert