python中struct模塊之字節(jié)型數(shù)據(jù)的處理方法
簡(jiǎn)介
這個(gè)模塊處理python中常見(jiàn)類型數(shù)據(jù)和Python bytes之間轉(zhuǎn)換。這可用于處理存儲(chǔ)在文件或網(wǎng)絡(luò)連接中的bytes數(shù)據(jù)以及其他來(lái)源。在python中沒(méi)有專門處理字節(jié)的數(shù)據(jù)類型,建立字節(jié)型數(shù)據(jù)也比較麻煩,我們知道的bytes()函數(shù)也只能對(duì)無(wú)符號(hào)整型做處理,并且數(shù)據(jù)如下(沒(méi)錯(cuò),數(shù)字為多少就有多少個(gè)\x00,我們要是用這種方式來(lái)存儲(chǔ)大量數(shù)據(jù),結(jié)果可想而知):
va = bytes(1) # va: '\x00' vb = bytes(2) # vb: '\x00\x00' vc = bytes(5) # vc: '\x00\x00\x00\x00\x00'
但在python中str類型中既可以用字符串表示也可以以字節(jié)方式表示,所以你定義一個(gè)字節(jié)型的字符串常量,python是能處理它的:
va = '\x26' # va: '&'
struct處理
字節(jié)順序
一個(gè)數(shù)據(jù)有多個(gè)字節(jié)表示的時(shí)候,字節(jié)的順序不同也就決定了值,在struct中有以下幾種字節(jié)順序:
字符 | 字節(jié)順序 | 尺寸 | 對(duì)齊方式 |
---|---|---|---|
@ | 本機(jī) | 本機(jī) | 本機(jī) |
= | 本機(jī) | 標(biāo)準(zhǔn) | 無(wú) |
< | 小端 | 標(biāo)準(zhǔn) | 無(wú) |
> | 大端 | 標(biāo)準(zhǔn) | 無(wú) |
! | 網(wǎng)絡(luò) | 標(biāo)準(zhǔn) | 無(wú) |
對(duì)于字節(jié)順序,只有大端和小端兩種方式,只是比如你用@和=代表你用本機(jī)的字節(jié)順序,!代表你使用網(wǎng)絡(luò)的字節(jié)順序。你不指定字節(jié)順序則默認(rèn)的是@。
本地字節(jié)順序是大端或小端,取決于主機(jī)系統(tǒng)。例如,Intel x86和AMD64(x86-64)是小端的; 摩托羅拉68000和PowerPC G5是大端; ARM和Intel Itanium具有可切換的字節(jié)序(雙字節(jié)序)。使用sys.byteorder來(lái)檢查你的系統(tǒng)的字節(jié)順序。
數(shù)據(jù)格式
struct支持的打包解包的數(shù)據(jù)格式如下,我們需要指定格式才能對(duì)應(yīng)處理,其中對(duì)應(yīng)尺寸已列出(以字節(jié)為單位):
字符 | C類型 | python類型 | 標(biāo)準(zhǔn)尺寸 |
---|---|---|---|
x | 填充字節(jié) | 沒(méi)有意義的值 | |
c | char | 長(zhǎng)度為1的字節(jié) | 1 |
b | signed char | 整型 | 1 |
B | unsigned char | 整型 | 1 |
? | _Bool | 布爾 | 1 |
h | short | 整型 | 2 |
H | unsigned short | 整型 | 2 |
i | int | 整型 | 4 |
I | unsigned int | 整型 | 4 |
l | long | 整型 | 4 |
L | unsigned long | 整型 | 4 |
q | long long | 整型 | 8 |
Q | unsigned long long | 整型 | 8 |
n | ssize_t | 整型 | |
N | size_t | 整型 | |
e | 浮動(dòng) | 2 | |
f | float | 浮動(dòng) | 4 |
d | double | 浮動(dòng) | 8 |
s | char[] | 字節(jié) | |
p | char[] | 字節(jié) | |
P | void * | 整型 |
打包
通過(guò)struct的pack(fmt, *args)來(lái)實(shí)現(xiàn)對(duì)各種數(shù)據(jù)的打包(轉(zhuǎn)換為對(duì)應(yīng)字節(jié)數(shù)據(jù)),pack的需要傳遞的參數(shù)fmt就是數(shù)據(jù)的格式,包括了字節(jié)順序、數(shù)據(jù)類型;后面的*args參數(shù)是需要打包的數(shù)據(jù)。
vaa = struct.pack('>I', 1255) # vaa: '\x00\x00\x04\xe7' 1*4=1個(gè)字節(jié) vab = struct.pack('>II', 1255, 23) # vab: '\x00\x00\x04\xe7\x00\x00\x00\x17' 2*4=8個(gè)字節(jié) vac = struct.pack('>2I?', 1255, 23, True) # vac: '\x00\x00\x04\xe7\x00\x00\x00\x17\x01' 2*4+1=9個(gè)字節(jié)
我們看上述三個(gè)使用例子(數(shù)據(jù)與數(shù)據(jù)之間沒(méi)有填充,都是連續(xù)的,比如對(duì)于vac我們不知道 它是由兩個(gè)4字節(jié)無(wú)符號(hào)整型和一個(gè)布爾構(gòu)成,我們就無(wú)法取得正確的值),看fmt參數(shù):
‘>I'代表了以大端的字節(jié)順序打包一個(gè)4字節(jié)無(wú)符號(hào)整型數(shù)據(jù),所以后面只跟了一個(gè)無(wú)符號(hào)整型參數(shù)1255;
‘>II'代表了以大端的字節(jié)順序打包兩個(gè)4字節(jié)無(wú)符號(hào)整型數(shù)據(jù),所以后面跟了兩個(gè)個(gè)無(wú)符號(hào)整型參數(shù)1255和23;
‘>2I?'代表了以大端的字節(jié)順序打包兩個(gè)4字節(jié)無(wú)符號(hào)整型和一個(gè)布爾型數(shù)據(jù),所以后面跟了兩個(gè)個(gè)無(wú)符號(hào)整型參數(shù)1255、23和一個(gè)布爾值True。
注意'2I'和'II','4I'和'IIII','2?'和'??'是一樣的效果。
解包
通過(guò)struct的unpack(fmt, string)來(lái)實(shí)現(xiàn)對(duì)字符串的解包,fmt和打包的是完全一樣的,如下(返回的結(jié)果是一個(gè)元組):
vaa = struct.pack('>I', 1255) # vaa: '\x00\x00\x04\xe7' vab = struct.pack('>II', 1255, 23) # vab: '\x00\x00\x04\xe7\x00\x00\x00\x17' vaaa = struct.unpack('>I', vaa) # vaaa: <class 'tuple'>: (1255, ) vaba = struct.unpack('>II', vab) # vaba: <class 'tuple'>: (1255, 23)
進(jìn)階使用
pack_into(fmt, buffer, offset, *args)
fmt參數(shù)和pack是一樣的,buffer參數(shù)是可寫的緩存區(qū),offset是寫入位置的偏移量,*args是需要寫入的數(shù)據(jù)。這個(gè)有什么用呢,我們想想這樣兩個(gè)情況,我們有兩個(gè)類型已經(jīng)打包好,我們想在這兩個(gè)已經(jīng)打包好的數(shù)據(jù)后面再添加一個(gè)數(shù)據(jù)打包;或者我們要打包的數(shù)據(jù)很多,我們不可能在pack中把所有需要打包的數(shù)據(jù)都通過(guò)參數(shù)傳遞給pack,那你的pack函數(shù)可能得寫成千上完個(gè)參數(shù)了。這時(shí)候我們就可以用到這個(gè)函數(shù)了。
要使用它必須要一個(gè)可以寫入的緩存區(qū),我們可以導(dǎo)入一個(gè)字符緩存區(qū)包,然后創(chuàng)建一個(gè)固定大小的緩存區(qū)(以字節(jié)為單位):
import struct from ctypes import create_string_buffer # 創(chuàng)建一個(gè)9字節(jié)大小的緩存區(qū),初始化默認(rèn)全部為\x00 buf = create_string_buffer(9) # buf.raw: '\x00\x00\x00\x00\x00\x00\x00\x00\x00' # 沖緩存區(qū)buf的第0個(gè)字節(jié)開(kāi)始打包兩個(gè)4字節(jié)無(wú)符號(hào)整型數(shù)據(jù)1和2 struct.pack_into(">II", buf, 0, 1, 2) # buf.raw: '\x00\x00\x00\x01\x00\x00\x00\x02\x00' # 然后我們想再打包一個(gè)布爾型數(shù)據(jù)到buf中就可以改變以下偏移量 struct.pack_into(">?", buf, 8, True) # buf.raw: '\x00\x00\x00\x01\x00\x00\x00\x02\x01'
unpack_from(fmt, buffer, offset)和calcsize(fmt)結(jié)合解包數(shù)據(jù)
calcsize用于計(jì)算格式字符串所對(duì)應(yīng)的結(jié)果的長(zhǎng)度,如:struct.calcsize(‘II'),返回8。因?yàn)閮蓚€(gè)無(wú)符號(hào)整型所占用的長(zhǎng)度是8個(gè)字節(jié)。unpack_from(fmt, buffer, offset)用于從buffer緩存區(qū)中使用fmt格式從offset偏移量處開(kāi)始解包fmt里對(duì)應(yīng)數(shù)量的數(shù)據(jù)。
import struct from ctypes import create_string_buffer buf = create_string_buffer(9) struct.pack_into(">II", buf, 0, 1, 2) struct.pack_into(">?", buf, 8, True) # 記錄位置 pos = 0 # 從buf緩存區(qū)中以大端方式從偏移位置pos處解包兩個(gè)無(wú)符號(hào)整型數(shù)據(jù)返回,注意 #返回值如果只寫一個(gè)則返回一個(gè)元組,否則你解包幾個(gè)數(shù)據(jù)就要寫幾個(gè)返回值。 val = struct.unpack_from('>II', buf, pos) # val: <class 'tuple'>: (1, 2) val_a, val_b = struct.unpack_from('>II', buf, pos) # val_a: 1 val_b: 2 # 重置解包位置 pos += struct.calcsize('>II') # pos: 8 val_c, = struct.unpack_from('>?', buf, pos) # val_c: True
示例
這個(gè)示例是基于mnist手寫數(shù)字識(shí)別的,我們剛開(kāi)始有60000張手寫數(shù)字的圖片(.bmp格式的),我們通過(guò)下述代碼將60000張圖片轉(zhuǎn)換成字節(jié)型數(shù)據(jù),bytes.py代碼如下:
import struct import os import numpy as np from ctypes import create_string_buffer import cv2 # 創(chuàng)建一個(gè)60000 * 784 * 1 + 3 * 4字節(jié)大小的緩存區(qū),初始化默認(rèn)全部為\x00 buffer = create_string_buffer(60000 * 784 * 1 + 3 * 4) def writeBytesData(): index = 0 BMP_NUM = 0 BMP_WIDTH = 28 BMP_HEIGHT = 28 # 先保留三個(gè)無(wú)符號(hào)整型的緩存區(qū) index += struct.calcsize('>III') path = 'data/bmp' if not os.path.exists(path): print('No this dir!') return list = os.listdir(path) for line_bmp in list: bmp_path = os.path.join(path, line_bmp) if os.path.isdir(bmp_path): print('This is not a .bmp') else: BMP_NUM += 1 print(BMP_NUM) buf = cv2.imread(bmp_path, cv2.IMREAD_GRAYSCALE) buf = np.reshape(buf, [784]) for pos in range(buf.__len__()): struct.pack_into('>B', buffer, index, buf[pos]) index += struct.calcsize('>B') # 將保留緩存區(qū)的內(nèi)容填上 struct.pack_into('>III', buffer, 0, BMP_NUM, BMP_WIDTH, BMP_HEIGHT) with open('data/bytes/bytes.bytes', 'wb') as fp: fp.write(buffer) def readFromBytes(): index = 0 images = [] with open('data/bytes/bytes.bytes', 'rb') as fp: buffer = fp.read() # 解包前三個(gè)無(wú)符號(hào)整型 bmp_num, bmp_width, bmp_height = struct.unpack_from('>III', buffer, index) # 重定位偏移量 index += struct.calcsize('>III') for pos in range(bmp_num): img = struct.unpack_from('>784B', buffer, index) index += struct.calcsize('>784B') # 修改為原來(lái)的圖片形狀 img = np.array(img, dtype=np.uint8) img = np.reshape(img, [bmp_height, bmp_width]) # 顯示圖片 cv2.imshow('bmp', img) # 按任意鍵繼續(xù) cv2.waitKey(0) images.append(img) return images writeBytesData() readFromBytes()
在寫入bytes文件的時(shí)候有點(diǎn)慢,由于有60000張圖片每張要寫28 * 28個(gè)字節(jié),其中目錄結(jié)構(gòu)如下,需要圖片的可以去我的下載區(qū)下載mnist圖片數(shù)據(jù)集:
bytes.py data bmp 1.bmp 2.bmp ... 60000.bmp bytes
以上這篇python中struct模塊之字節(jié)型數(shù)據(jù)的處理方法就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
在python中使用pyspark讀寫Hive數(shù)據(jù)操作
這篇文章主要介紹了在python中使用pyspark讀寫Hive數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06Python Sql數(shù)據(jù)庫(kù)增刪改查操作簡(jiǎn)單封裝
這篇文章主要為大家介紹了Python Sql數(shù)據(jù)庫(kù)增刪改查操作簡(jiǎn)單封裝,感興趣的小伙伴們可以參考一下2016-04-04python KNN算法實(shí)現(xiàn)鳶尾花數(shù)據(jù)集分類
這篇文章主要介紹了python KNN算法實(shí)現(xiàn)鳶尾花數(shù)據(jù)集分類,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10Python3實(shí)現(xiàn)將本地JSON大數(shù)據(jù)文件寫入MySQL數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Python3實(shí)現(xiàn)將本地JSON大數(shù)據(jù)文件寫入MySQL數(shù)據(jù)庫(kù)的方法,涉及Python針對(duì)json大數(shù)據(jù)文件的逐行讀取、mysql數(shù)據(jù)庫(kù)寫入等相關(guān)操作技巧,需要的朋友可以參考下2018-06-06淺析python中的絕對(duì)導(dǎo)入和相對(duì)導(dǎo)入
這篇文章主要是想和大家簡(jiǎn)單聊聊python中絕對(duì)導(dǎo)入和相對(duì)導(dǎo)入的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考下2023-09-09Python使用pylab庫(kù)實(shí)現(xiàn)畫線功能的方法詳解
這篇文章主要介紹了Python使用pylab庫(kù)實(shí)現(xiàn)畫線功能的方法,結(jié)合具體實(shí)例分析了Python使用pylab庫(kù)的相關(guān)函數(shù)實(shí)現(xiàn)畫線功能的操作技巧,并附帶說(shuō)明了相關(guān)函數(shù)與參數(shù)功能,需要的朋友可以參考下2017-06-06VPS CENTOS 上配置python,mysql,nginx,uwsgi,django的方法詳解
這篇文章主要介紹了VPS CENTOS 上配置python,mysql,nginx,uwsgi,django的方法,較為詳細(xì)的分析了VPS CENTOS 上配置python,mysql,nginx,uwsgi,django的具體步驟、相關(guān)命令與操作注意事項(xiàng),需要的朋友可以參考下2019-07-07