瞭解c語言的人,必定會知道struct結構體在c語言中的做用,它定義了一種結構,裏面包含不一樣類型的數據(int,char,bool等等),方便對某一結構對象進行處理。而在網絡通訊當中,大多傳遞的數據是以二進制流(binary data)存在的。當傳遞字符串時,沒必要擔憂太多的問題,而當傳遞諸如int、char之類的基本數據的時候,就須要有一種機制將某些特定的結構體類型打包成二進制流的字符串而後再網絡傳輸,而接收端也應該能夠經過某種機制進行解包還原出原始的結構體數據。python中的struct模塊就提供了這樣的機制,該模塊的主要做用就是對python基本類型值與用python字符串格式表示的C struct類型間的轉化。再者, 有的時候須要用python處理二進制數據,好比,存取文件,socket操做時.這時候,可使用python的struct模塊來完成.能夠用 struct來處理c語言中的結構體. struct模塊中最重要的三個函數是pack(), unpack(), calcsize()python
# 按照給定的格式(fmt),把數據封裝成字符串(其實是相似於c結構體的字節流) 網絡
pack(fmt, v1, v2, ...) app
# 按照給定的格式(fmt)解析字節流string,返回解析出來的tuple socket
unpack(fmt, string) 函數
# 計算給定的格式(fmt)佔用多少字節的內存 ui
calcsize(fmt)google
注1.q和Q只在機器支持64位操做時有意思spa
注2.每一個格式前能夠有一個數字,表示個數指針
注3.s格式表示必定長度的字符串,4s表示長度爲4的字符串,可是p表示的是pascal字符串code
注4.P用來轉換一個指針,其長度和機器字長相關
注5.最後一個能夠用來表示指針類型的,佔4個字節
一個format字符串是用數字和上述定義的字符來表示的。如 5i
表示5個 int 型數,H
表示一個 unsigned short 類型的數, 0Q
則表示0個 unsigned long long 類型的數。在上面的表格中,若是表示了字符表明的位數,則這個字符前面再加數字,表示多個變量,如 3Q
表示3個變量;若是沒有指明字符表明的位數,則前面加數據表明一個變量,如 3s
表示一個長度爲3的字符串,在pack或者unpack的時候須要注意這一點。
在與其它語言進行數據交互時,一般還要考慮的事情是:編譯器是否有字節對齊?大端存儲仍是小端存儲?在format字段串的第一個字符能夠用來定義數據的對齊方式:
例如:
struct.unpack('>I', a)[0]
format=">I"是什麼意思,從google找了一下,有人說這個東西,可都是比較籠統,沒能讓我明白,因而硬着頭皮看API:
By default, C numbers are represented in the machine’s native format and byte order, and properly aligned by skipping pad bytes if necessary (according to the rules used by the C compiler).
一般,C語言下數字都是機器語言的格式而且按照字節排序,同時在須要的狀況下會利用跳過填補的字節來進行適當的調整
Alternatively, the first character of the format string can be used to indicate the byte order, size and alignment of the packed data。
非此即彼:字符串的第一個字符要麼被用於表示字符串的字節的排序,或者是字符串的size,還有就是數據是否對準。
Native byte order is big-endian or little-endian, depending on the host system. For example, Motorola and Sun processors are big-endian; Intel and DEC processors are little-endian.
計算機的字節序要麼是高位順序,要麼是低位的,這依賴於主機自己。好比,摩托羅拉和sun的處理器是高位的,可是intel和DEC的是低位的。
這樣子就明白了上面的format=">I"的意思,也就是說按照高位順序來格式化取得一個int或long值。下面問題就又來了,你怎麼知道讀取的就是一個int或long值呢?
經過看struct的文檔,能夠看到struct經過兩張表制定了必定的format規則,我按照本身的觀察,給他概括爲兩類,一個是和C當中類型的對照,另外一個就是選擇按照高位仍是低位來解釋字節。上面已經說了高低字節順序,那麼觀察和C對照的表格,發現I 表明的就是integer or long ,詳細的能夠去看python的API。
一個C結構體:
struct Header { unsigned short id; char[4] tag; unsigned int version; unsigned int count; }
如今接收到一個上述結果體數據,能夠用unpack函數解析:
import struct (id, tag, version, count) = struct.unpack('!H4s2I', s)
在上述代碼中,H對應id,4s對應tag,2I對應version和count。!表示是由網絡字節順序傳輸。
這樣,經過unpack,就解出對應的信息。一樣,經過pack也能夠將信息打包也對應的二進制格式:
import struct ss = struct.pack("!H4s2I", id, tag, version, count);
pack函數將信息打包成一個字符串,其實是類C結果體字節流,表示的就是一個Header結構體。
import struct a=12.34 #將a變爲二進制 bytes=struct.pack('i',a)
pack後,bytes就是一個str字符串,內容與a的二進制存儲內容相同
能夠用以下代碼進行反轉換:
(a,) = struct.unpack('i', bytes)
注意:unpack返回的是一個tuple。
# 使用struck.unpack獲取子字符串 ,取前5個字符,跳過4個字符華,再取3個字符
format = '5s 4x 3s'
print struct.unpack(format, 'Test astring')
#('Test', 'ing')
來個簡單的例子吧,有一個字符串'He is not very happy',處理一下,把中間的not去掉,而後再輸出。
import struct
theString = 'He is not very happy'
format = '2s 1x 2s 5x 4s 1x 5s'
print ' '.join(struct.unpack(format, theString))
輸出結果:
He is very happy
隨後是關於網絡字節的東東,從網上看來的,感受有用:
Python的socket庫採用string類型來發送和接收數據,這樣當咱們用
i = socket.recv(4)
來接收一個4字節的整數時,該整數其實是以二進制的形式保存在字符串 i 的前4個字節中;大多數的時候咱們須要的是一個真正的integer/long型,而不是一個用string型表示的整型。這時咱們可使用struct庫:Interpret
strings as packed binary data. 對上面的狀況,咱們能夠寫
t = unpack("I", i)
第一個參數是格式化字符串,I指明字符串 i 包含的頭一個數據項是一個以C語言的unsigned integer表示的整數,這裏 i 只包含了一個數據項,實際上這個被解釋的字符串也能夠包含多個數據項,只要在格式化字符串裏爲每項數據指明一個格式便可;天然地,unpack返回的就是一個tuple類型了。
1. struct.pack時可能會遇到:> struct.error: pack requires exactly 2 arguments
這一錯誤說明format中的參數個數與實際輸入的參數個數不符。如 bytes = struct.pack('2I', a)
須要2個參數而只輸入了一個
2. struct.unpack時可能會遇到:> struct.error: unpack requires a string argument of length *
這一錯誤說明format中參數表明的數據佔用內存數,與輸入的數據佔用的內存長度不符。這時候能夠用 struct.calcsize(fmt)
函數檢查format字符表明的長度,及用len(string)函數來檢測數據的長度。