Python如何利用struct進(jìn)行二進(jìn)制文件或數(shù)據(jù)流
利用struct進(jìn)行二進(jìn)制文件或數(shù)據(jù)流操作
在實(shí)際工作場(chǎng)景中,特別是在嵌入式開(kāi)發(fā)過(guò)程中,經(jīng)常需要和二進(jìn)制操作打交道。
這里舉兩個(gè)典型例子:
設(shè)備上的一些配置參數(shù)通常是以二進(jìn)制的形式保存在Flash等非易失存儲(chǔ)芯片中,而配置參數(shù)的格式與內(nèi)容編輯工作通常是在PC上中完成,將生成的xxx.bin文件下載到設(shè)備并寫入Flash中,設(shè)備啟動(dòng)后從Flash固定地址讀取配置到內(nèi)存中使用。如果能夠在PC上以某種腳本的形式方便的對(duì)二進(jìn)制文件的格式和內(nèi)容進(jìn)行編輯,或者將二進(jìn)制文件解析為方便閱讀的格式,可以提升很多工作效率,使用起來(lái)也非常方便
設(shè)備和管理端(上位機(jī)、服務(wù)器等)需要通過(guò)一定的接口進(jìn)行數(shù)據(jù)交互,操作人員通過(guò)管理端訪問(wèn)和控制設(shè)備,例如使用UART進(jìn)行串口通信,或者使用Ethernet接口進(jìn)行UDP/TCP甚至應(yīng)用層協(xié)議通信。無(wú)論使用什么接口,都需要對(duì)業(yè)務(wù)數(shù)據(jù)進(jìn)行協(xié)議封裝,使用二進(jìn)制的數(shù)據(jù)流封裝是一個(gè)非常普遍的選擇,尤其是在資源非常緊張的嵌入式平臺(tái)上,與使用字符串或JSON格式傳輸數(shù)據(jù)的協(xié)議(例如HTTP)相比,二進(jìn)制占用的帶寬和內(nèi)存資源較少。另外,很多嵌入式平臺(tái)上由于資源限制(內(nèi)存、庫(kù)函數(shù)支持等),并不能很好的集成復(fù)雜協(xié)議的很多要求,反而是二進(jìn)制格式是C語(yǔ)言原生支持,也更方便移植
Python作為一個(gè)靈活的腳本語(yǔ)言,在運(yùn)行環(huán)境搭建、編碼和調(diào)試方面都很便利,而且有豐富的第三方庫(kù)支持,通常只需要很少量的代碼就能夠輕松完成很多工作。
以下內(nèi)容分別從二進(jìn)制文件和二進(jìn)制數(shù)據(jù)流方面對(duì)Python中struct的用法進(jìn)行說(shuō)明
struct進(jìn)行二進(jìn)制操作的用法
struct是Python內(nèi)置的一個(gè)模塊,struct對(duì)數(shù)據(jù)格式的封裝能夠與C語(yǔ)言進(jìn)行非常方便的適配,能夠完美的支持C語(yǔ)言原生的數(shù)據(jù)類型,以及指定字節(jié)順序。
以下表格是struct封裝格式與C語(yǔ)言數(shù)據(jù)類型的對(duì)應(yīng)關(guān)系
| Format | C Type | Python Type | Size |
|---|---|---|---|
| x | pad byte | no value | 1 |
| c | char | string of length 1 | 1 |
| b | signed char | integer | 1 |
| B | unsigned char | integer | 1 |
| ? | _Bool | bool | 1 |
| h | short | integer | 2 |
| H | unsigned short | integer | 2 |
| i | int | integer | 4 |
| I | unsigned int | integer | 4 |
| l | long | integer | 4 |
| L | unsigned long | integer | 4 |
| q | long long | integer | 8 |
| Q | unsigned long long | integer | 8 |
| f | float | float | 4 |
| d | double | float | 8 |
| s | char[] | string | |
| p | char[] | string | |
| p | void * | integer |
struct支持指定字節(jié)序,也是通過(guò)參數(shù)的方式指定的
| Code | Meaning |
| @ | Native order |
| = | Native standard |
| < | Little-endian |
| > | Big-endian |
| ! | Network order |
怎么用?
struct提供了一些函數(shù)來(lái)封裝數(shù)據(jù)
pack(fmt, v1, v2, ...):按照給定的格式fmt,將數(shù)據(jù)v1和v2封裝rdata = unpack(fmt, data):按照給定的格式fmt,將源數(shù)據(jù)data解析為元組形式的rdatacalcsize(fmt):計(jì)算給定格式占用的內(nèi)存字節(jié)數(shù)pack_into(fmt, buffer, offset, v1, v2…):按照給定的格式fmt將數(shù)據(jù)以追加的方式封裝,寫入以offset開(kāi)始的buffer中rdata = unpack_from(fmt, buffer, offset):按照給定的格式fmt解析以offset開(kāi)始的緩沖區(qū)buffer,并返回解析結(jié)果
這些函數(shù)接口中的"fmt",是字符串的形式,其內(nèi)容就是上文中兩個(gè)表格的第一列。
例如:以下代碼將16進(jìn)制字"0x1516"分別以小端和大端形式封裝為x1和x2
import struct
x1 = struct.pack('H', 0x1516)
x2 = struct.pack('>H', 0x1516)
print('x1:{} x2:{}'.format(x1, x2))
# running result:
# x1:b'\x15\x13' x2:b'\x13\x15'參數(shù)中的"fmt"可以是單獨(dú)某一種格式,也可以是多種格式的混合
例如:以下代碼將一個(gè)十六進(jìn)制字節(jié)"0xaa"和雙字"0x11223344"合并封裝為x,然后再解析為
import struct
x = struct.pack('BI', 0xaa, 0x11223344)
d1, d2 = struct.unpack('BI', x)
print('pack:{} unpack:{:#x} {:#x}'.format(x, d1, d2))
# running result:
# pack:b'\xaa\x00\x00\x00D3"\x11' unpack:0xaa 0x11223344二進(jìn)制文件操作
利用struct進(jìn)行二進(jìn)制文件編輯,有以下關(guān)鍵點(diǎn)
- 使用ctypes.create_string_buffer創(chuàng)建buffer
- 使用pack_into進(jìn)行追加封裝
- 文件打開(kāi)使用'b'標(biāo)志表示以二進(jìn)制方式寫入
以下例子利用struct封裝一個(gè)固定格式的數(shù)據(jù)到bin文件,并讀出解析
# python3.7.6
import struct
from ctypes import create_string_buffer
"""
Python struct example
package a format into binary finle
format define:
31 15 0
+--------------------------------+
| mark |
+----------------+---------------+
| version | length |
+----------------+---------------+
| checksum |
+--------------------------------+
| data... |
+--------------------------------+
"""
def dump(mark, version, length, data, checksum):
print('\n---- dump ----')
print('mark:{:#X}'.format(mark))
print('version:{}.{}'.format(version['major'], version['minor']))
print('data length:{}'.format(length))
print('checksum:{}'.format(checksum))
print('data:{}'.format(data))
print('--------------\n')
def save_bin(binary_file='test.bin', mark=0x5A5A5A5A, version={'major':1, 'minor':0}):
offset = 0
checksum = 0
dataLength = 0
data = [1,2,3,4,5,6,7,8]
# create buffer
buffer_size = 4+2+2+4+len(data)*4
buffer = create_string_buffer(buffer_size)
print('create buffer with {}byte'.format(buffer_size))
# pack mark+version+length
fmt = 'IBBH'
dataLength = len(data)
struct.pack_into(fmt, buffer, offset, mark, version['major'], version['minor'], dataLength)
offset = offset + struct.calcsize(fmt)
# pack data
fmt = 'I'
offset = offset + 4 # skip checksum
for x in data:
struct.pack_into(fmt, buffer, offset, x)
offset = offset + struct.calcsize(fmt)
# pack checksum
fmt = 'I'
offset = 8
for x in buffer:
d = x[0]
checksum = checksum + d
struct.pack_into(fmt, buffer, offset, checksum)
# write to file
f = open(binary_file, 'wb')
f.write(buffer)
f.close()
dump(mark, version, dataLength, data, checksum)
print('{}byte write to {}'.format(len(buffer), binary_file))
def read_bin(binary_file='test.bin'):
offset = 0
data = []
# open and read file
fmt = 'IBBHI'
f = open(binary_file, 'rb')
buffer = f.read()
mark, major, minor, length, checksum = struct.unpack_from(fmt, buffer, 0)
version = {'major':major, 'minor':minor}
offset = struct.calcsize(fmt)
for i in range(length-1):
offset = offset + 4
d = struct.unpack_from('I', buffer, offset)
data.append(d[0])
dump(mark, version, length, data, checksum)
print('read {}byte from {}'.format(len(buffer), binary_file))
save_bin()
read_bin()
"""
running result:
create buffer with 44byte
---- dump ----
mark:0X5A5A5A5A
version:1.0
data length:8
checksum:405
data:[1, 2, 3, 4, 5, 6, 7, 8]
--------------
44byte write to test.bin
---- dump ----
mark:0X5A5A5A5A
version:1.0
data length:8
checksum:405
data:[2, 3, 4, 5, 6, 7, 8]
--------------
read 44byte from test.bin
"""總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解析Pytorch中的torch.gather()函數(shù)
本文給大家介紹了Pytorch中的torch.gather()函數(shù),通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-11-11
pyqt5利用pyqtDesigner實(shí)現(xiàn)登錄界面
這篇文章主要為大家詳細(xì)介紹了pyqt5利用pyqtDesigner實(shí)現(xiàn)登錄界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03
TensorFlow通過(guò)文件名/文件夾名獲取標(biāo)簽,并加入隊(duì)列的實(shí)現(xiàn)
今天小編就為大家分享一篇TensorFlow通過(guò)文件名/文件夾名獲取標(biāo)簽,并加入隊(duì)列的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02
Python爬取哆啦A夢(mèng)-伴我同行2豆瓣影評(píng)并生成詞云圖
哆啦A夢(mèng)系列是陪伴我,乃至陪伴了幾代人成長(zhǎng)的故事.50年來(lái),藤子·F·不二雄先生創(chuàng)造了竹蜻蜓,任意門,時(shí)光機(jī)器等等無(wú)數(shù)的新奇道具,讓大雄和他的小伙伴們經(jīng)歷了各種冒險(xiǎn),也經(jīng)歷了許多充滿戲劇性的啼笑皆非的日常.特意寫了這篇文章,教大家怎么繪制詞云圖,需要的朋友可以參考下2021-06-06
Python中pandas庫(kù)的常用操作實(shí)例
這篇文章主要介紹了Python中pandas庫(kù)的常用操作實(shí)例,Pandas 庫(kù)是一個(gè)免費(fèi)、開(kāi)源的第三方 Python 庫(kù),是 Python 數(shù)據(jù)分析必不可少的工具之一,它為 Python 數(shù)據(jù)分析提供了高性能,且易于使用的數(shù)據(jù)結(jié)構(gòu),即 Series 和 DataFrame,需要的朋友可以參考下2023-07-07
python list 查詢是否存在并且并返回下標(biāo)的操作
這篇文章主要介紹了python list 查詢是否存在并且并返回下標(biāo)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05

