python struct.unpack

最近在學(xué)習(xí)python網(wǎng)絡(luò)編程這一塊,在寫簡(jiǎn)單的socket通信代碼時(shí),遇到了struct這個(gè)模塊的使用,當(dāng)時(shí)不太清楚這到底有和作用,后來查閱了相關(guān)資料大概了解了,在這里做一下簡(jiǎn)單的總結(jié)。

? ? 了解c語言的人,一定會(huì)知道struct結(jié)構(gòu)體在c語言中的作用,它定義了一種結(jié)構(gòu),里面包含不同類型的數(shù)據(jù)(int,char,bool等等),方便對(duì)某一結(jié)構(gòu)對(duì)象進(jìn)行處理。而在網(wǎng)絡(luò)通信當(dāng)中,大多傳遞的數(shù)據(jù)是以二進(jìn)制流(binary data)存在的。當(dāng)傳遞字符串時(shí),不必?fù)?dān)心太多的問題,而當(dāng)傳遞諸如int、char之類的基本數(shù)據(jù)的時(shí)候,就需要有一種機(jī)制將某些特定的結(jié)構(gòu)體類型打包成二進(jìn)制流的字符串然后再網(wǎng)絡(luò)傳輸,而接收端也應(yīng)該可以通過某種機(jī)制進(jìn)行解包還原出原始的結(jié)構(gòu)體數(shù)據(jù)。python中的struct模塊就提供了這樣的機(jī)制,該模塊的主要作用就是對(duì)python基本類型值與用python字符串格式表示的C struct類型間的轉(zhuǎn)化(This module performs conversions between Python values and C structs represented as Python strings.)。stuct模塊提供了很簡(jiǎn)單的幾個(gè)函數(shù),下面寫幾個(gè)例子。

1、基本的pack和unpack

? ? struct提供用format specifier方式對(duì)數(shù)據(jù)進(jìn)行打包和解包(Packing and Unpacking)。例如:

import struct

import binascii

values = (1, 'abc', 2.7)

s = struct.Struct('I3sf')

packed_data = s.pack(*values)

unpacked_data = s.unpack(packed_data)

print 'Original values:', values

print 'Format string :', s.format

print 'Uses :', s.size, 'bytes'

print 'Packed Value :', binascii.hexlify(packed_data)

print 'Unpacked Type :', type(unpacked_data), ' Value:', unpacked_data

復(fù)制

輸出:

Original values: (1, 'abc', 2.7) Format string : I3sf Uses : 12 bytes Packed Value : 0100000061626300cdcc2c40 Unpacked Type :? Value: (1, 'abc', 2.700000047683716)

代碼中,首先定義了一個(gè)元組數(shù)據(jù),包含int、string、float三種數(shù)據(jù)類型,然后定義了struct對(duì)象,并制定了format‘I3sf’,I 表示int,3s表示三個(gè)字符長(zhǎng)度的字符串,f 表示 float。最后通過struct的pack和unpack進(jìn)行打包和解包。通過輸出結(jié)果可以發(fā)現(xiàn),value被pack之后,轉(zhuǎn)化為了一段二進(jìn)制字節(jié)串,而unpack可以把該字節(jié)串再轉(zhuǎn)換回一個(gè)元組,但是值得注意的是對(duì)于float的精度發(fā)生了改變,這是由一些比如操作系統(tǒng)等客觀因素所決定的。打包之后的數(shù)據(jù)所占用的字節(jié)數(shù)與C語言中的struct十分相似。定義format可以參照官方api提供的對(duì)照表:

2、字節(jié)順序

另一方面,打包的后的字節(jié)順序默認(rèn)上是由操作系統(tǒng)的決定的,當(dāng)然struct模塊也提供了自定義字節(jié)順序的功能,可以指定大端存儲(chǔ)、小端存儲(chǔ)等特定的字節(jié)順序,對(duì)于底層通信的字節(jié)順序是十分重要的,不同的字節(jié)順序和存儲(chǔ)方式也會(huì)導(dǎo)致字節(jié)大小的不同。在format字符串前面加上特定的符號(hào)即可以表示不同的字節(jié)順序存儲(chǔ)方式,例如采用小端存儲(chǔ) s = struct.Struct(‘<I3sf’)就可以了。官方api library 也提供了相應(yīng)的對(duì)照列表:

3、利用buffer,使用pack_into和unpack_from方法

? 使用二進(jìn)制打包數(shù)據(jù)的場(chǎng)景大部分都是對(duì)性能要求比較高的使用環(huán)境。而在上面提到的pack方法都是對(duì)輸入數(shù)據(jù)進(jìn)行操作后重新創(chuàng)建了一個(gè)內(nèi)存空間用于返回,也就是說我們每次pack都會(huì)在內(nèi)存中分配出相應(yīng)的內(nèi)存資源,這有時(shí)是一種很大的性能浪費(fèi)。struct模塊還提供了pack_into() 和 unpack_from()的方法用來解決這樣的問題,也就是對(duì)一個(gè)已經(jīng)提前分配好的buffer進(jìn)行字節(jié)的填充,而不會(huì)每次都產(chǎn)生一個(gè)新對(duì)象對(duì)字節(jié)進(jìn)行存儲(chǔ)。

import struct

import binascii

import ctypes

values = (1, 'abc', 2.7)

s = struct.Struct('I3sf')

prebuffer = ctypes.create_string_buffer(s.size)

print 'Before :',binascii.hexlify(prebuffer)

s.pack_into(prebuffer,0,*values)

print 'After pack:',binascii.hexlify(prebuffer)

unpacked = s.unpack_from(prebuffer,0)

print 'After unpack:',unpacked

復(fù)制

輸出:

Before : 000000000000000000000000 After pack: 0100000061626300cdcc2c40 After unpack: (1, 'abc', 2.700000047683716) 對(duì)比使用pack方法打包,pack_into 方法一直是在對(duì)prebuffer對(duì)象進(jìn)行操作,沒有產(chǎn)生多余的內(nèi)存浪費(fèi)。另外需要注意的一點(diǎn)是,pack_into和unpack_from方法均是對(duì)string buffer對(duì)象進(jìn)行操作,并提供了offset參數(shù),用戶可以通過指定相應(yīng)的offset,使相應(yīng)的處理變得更加靈活。例如,我們可以把多個(gè)對(duì)象pack到一個(gè)buffer里面,然后通過指定不同的offset進(jìn)行unpack:

import struct

import binascii

import ctypes

values1 = (1, 'abc', 2.7)

values2 = ('defg',101)

s1 = struct.Struct('I3sf')

s2 = struct.Struct('4sI')

prebuffer = ctypes.create_string_buffer(s1.size+s2.size)

print 'Before :',binascii.hexlify(prebuffer)

s1.pack_into(prebuffer,0,*values1)

s2.pack_into(prebuffer,s1.size,*values2)

print 'After pack:',binascii.hexlify(prebuffer)

print s1.unpack_from(prebuffer,0)

print s2.unpack_from(prebuffer,s1.size)

復(fù)制

輸出:

Before : 0000000000000000000000000000000000000000 After pack: 0100000061626300cdcc2c406465666765000000 (1, 'abc', 2.700000047683716) ('defg', 101)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容