2016-01-13 52 views
4

我試圖設計一個系統來對不同的二進制標誌作出反應。Python的結構錯誤

0 = Error 
1 = Okay 
2 = Logging 
3 = Number 

該數據的序列表示引用工作,標誌和編號的唯一ID。除了號碼標誌以外,一切正常。這是我得到...

>>> import struct 
>>> data = (1234, 3, 12345678) 
>>> bin = struct.pack('QHL', *data) 
>>> print(bin) 
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00Na\xbc\x00\x00\x00\x00\x00' 
>>> result = struct.unpack_from('QH', bin, 0) 
>>> print(result) 
(1234, 3) 
>>> offset = struct.calcsize('QH') 
>>> result += struct.unpack_from('L', bin, offset) 
>>> print(result) 
(1234, 3, 7011541669862440960) 

長應該是足夠大的代表人數12345678,但爲什麼它不正確解壓?

編輯:

當我嘗試獨立包裝,看起來結構是將國旗和長期之間存在太多的空字節。

>>> import struct 
>>> struct.pack('QH', 1234, 3) 
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00' 
>>> struct.pack('L', 12345678) 
b'Na\xbc\x00\x00\x00\x00\x00' 

我可以通過在長時間之前添加填充來重現此錯誤。

>>> struct.unpack('L', struct.pack('L', 12345678)) 
(12345678,) 
>>> struct.unpack('xL', struct.pack('xL', 12345678)) 
(12345678,) 
>>> struct.pack('xL', 12345678) 
b'\x00\x00\x00\x00\x00\x00\x00\x00Na\xbc\x00\x00\x00\x00\x00' 

可能的修復?

當我使用小端順序時,問題似乎自行糾正,並使二進制字符串更短。由於這是一個SSL封裝的TCP套接字,這是一個雙贏,對吧?保持低帶寬通常很好,是嗎?

>>> import struct 
>>> data = (1234, 3, 12345678) 
>>> bin = struct.pack('<QHL', *data) 
>>> print(bin) 
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00Na\xbc\x00' 
>>> result = struct.unpack_from('<QH', bin, 0) 
>>> print(result) 
(1234, 3) 
>>> offset = struct.calcsize('<QH') 
>>> result += struct.unpack_from('<L', bin, offset) 
>>> print(result) 
(1234, 3, 12345678) 

爲什麼會發生這種情況?我很困惑。

+0

您不關心它是否打開包裝太不正確? –

+0

@CaptPlanet我不知道如何測試。你能解釋一下嗎? – bkvaluemeal

+0

我的意思是說你已經提供瞭解壓輸出(1234,1)和(1234,1,7011541669862440960),其中標誌值應該是3.我猜測這是一個錯字。 –

回答

6

您正在運行字節對齊問題。您需要知道,默認情況下,結構的各個部分並不僅僅是彼此靠近放置,而是在內存中正確對齊。這使得它更有效率,尤其是對於其他應用程序,因爲它們有更直接的方式從它訪問單個字節,而不必考慮重疊。

您可以輕鬆地通過使用struct.calcsize看到使用的格式來編碼所需的必要空間看到這一點:

>>> struct.calcsize('QHL') 
16 
>>> struct.calcsize('QH') 
10 

正如你可以看到QHL需要16個字節,但QH需要10.我們不放過L但是隻有4個字節寬。所以有一些填充,確保L重新開始「一個新的塊」。這是因爲任何類型都需要(使用填充)從一個它自己大小的倍數的偏移處開始。對於QH它看起來像這樣:

QQ QQ | QQ QQ | HH 

一旦使用QHL,你會得到如下:

QQ QQ | QQ QQ | HH 00 | LL LL 

正如你可以看到,有兩個填充字節添加到確保L開始上新的四塊。

您可以在格式字符串的開頭使用特殊字符修改對齊方式(以及字節順序)。在你的情況,你可以使用=QHL完全禁用對齊:

QQ QQ | QQ QQ | HH LL | LL 

當我使用little-endian順序,這個問題似乎改正,使二進制串短。由於這是一個SSL封裝的TCP套接字,這是一個雙贏,對吧?保持低帶寬通常很好,是嗎?

使用明確的字節順序also disables alignment是的,這就是效果來自哪裏。但是,如果這是一個好主意,轉向對齊取決於。如果你想在其他地方使用你的數據,在其他程序中,堅持本地對齊將是一個好主意。

+0

這些數據將被封裝在一臺使用Python的計算機上,並且在使用Python的另一臺計算機上解壓縮。那個男人的路線不重要嗎? – bkvaluemeal

+0

只要你在所有機器上使用相同的平臺獨立格式,那麼是的,禁用對齊是好的。 – poke

1

正確的輸出,你的情況:

>>> import struct 
>>> data = (1234, 3, 12345678) 
>>> bin = struct.pack('QHL', *data) 
>>> print(bin) 
b'\xd2\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00Na\xbc\x00\x00\x00\x00\x00' 
>>> result = struct.unpack_from('QH', bin, 0) 
>>> print(result) 
(1234, 3) 
>>> result += struct.unpack_from('L', bin, 16) 
>>> print(result) 
(1234, 3, 12345678) 

這是因爲:

填充是連續的結構成員之間只有自動添加。

另外,你的修補程序適用的理由是:

沒有填充用非本地大小和對齊方式,例如,當添加'<','>','='和'!'。

1

這是一個字節對齊問題。在你的情況下,以下更換將給你正確的輸出。

result += struct.unpack_from('L', bin, offset+2)