2014-05-06 22 views
0

我正面臨一個着名的struct.pack的小角落案例。python轉換成int的任意字節數

情況如下:我有一個dll與python的薄層包裝。包裝中的一個python方法接受一個字節數組作爲參數。該字節數組表示特定硬件總線上寄存器的表示。每個總線具有不同的寄存器寬度,通常爲8,16和24位寬(在所有情況下對齊相同)。

當調用這個方法時,我需要將我的值(不管是什麼)轉換爲8/16或24位的字節數組。這種轉換是8或16位使用struct.pack相對容易:

byteList = struct.pack('>B', regValue) # For 8 bits case 
byteList = struct.pack('>H', regValue) # for 16 bits case 

我現在希望使其成爲所有三種情況下8/16 & 24位不夠靈活。我可以混合使用前兩行來處理這三種情況;但我覺得它很難看。

我希望這會工作:

packformat = ">{0}B".format(regSize) 
byteList = struct.pack(packformat, regValue) 

但它不是如此,因爲struct.pack期望的參數等量。

任何想法如何可以(整齊地)我的寄存器值轉換爲任意數量的字節?

回答

2

您總是打包未簽名的整數,只有大端才能開機。看看你打包時會發生什麼:

>>> import struct 
>>> struct.pack('>B', 255) 
'\xff' 
>>> struct.pack('>H', 255) 
'\x00\xff' 
>>> struct.pack('>I', 255) 
'\x00\x00\x00\xff' 

本質上,該值在開始時用空字節填充。要利用你的優勢:

>>> struct.pack('>I', 255)[-3:] 
'\x00\x00\xff' 
>>> struct.pack('>I', 255)[-2:] 
'\x00\xff' 
>>> struct.pack('>I', 255)[-1:] 
'\xff' 

你現在不會得到一個例外,如果你的價值太大,但它會極大地簡化你的代碼。您可以隨時添加一個單獨的驗證步驟:

def packRegister(value, size): 
    if value < 0 or value.bit_length() > size: 
     raise ValueError("Value won't fit in register of size {} bits".format(size)) 
    return struct.pack('>I', value)[-(size // 8):] 

演示:

>>> packRegister(255, 8) 
'\xff' 
>>> packRegister(1023, 16) 
'\x03\xff' 
>>> packRegister(324353, 24) 
'\x04\xf3\x01' 
>>> packRegister(324353, 8) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in packRegister 
ValueError: Value won't fit in register of size 8 bits 
+0

這是高明......和完美的作品:) – TocToc