Python如何利用struct進行二進制文件或數(shù)據(jù)流
利用struct進行二進制文件或數(shù)據(jù)流操作
在實際工作場景中,特別是在嵌入式開發(fā)過程中,經(jīng)常需要和二進制操作打交道。
這里舉兩個典型例子:
設(shè)備上的一些配置參數(shù)通常是以二進制的形式保存在Flash等非易失存儲芯片中,而配置參數(shù)的格式與內(nèi)容編輯工作通常是在PC上中完成,將生成的xxx.bin文件下載到設(shè)備并寫入Flash中,設(shè)備啟動后從Flash固定地址讀取配置到內(nèi)存中使用。如果能夠在PC上以某種腳本的形式方便的對二進制文件的格式和內(nèi)容進行編輯,或者將二進制文件解析為方便閱讀的格式,可以提升很多工作效率,使用起來也非常方便
設(shè)備和管理端(上位機、服務(wù)器等)需要通過一定的接口進行數(shù)據(jù)交互,操作人員通過管理端訪問和控制設(shè)備,例如使用UART進行串口通信,或者使用Ethernet接口進行UDP/TCP甚至應(yīng)用層協(xié)議通信。無論使用什么接口,都需要對業(yè)務(wù)數(shù)據(jù)進行協(xié)議封裝,使用二進制的數(shù)據(jù)流封裝是一個非常普遍的選擇,尤其是在資源非常緊張的嵌入式平臺上,與使用字符串或JSON格式傳輸數(shù)據(jù)的協(xié)議(例如HTTP)相比,二進制占用的帶寬和內(nèi)存資源較少。另外,很多嵌入式平臺上由于資源限制(內(nèi)存、庫函數(shù)支持等),并不能很好的集成復(fù)雜協(xié)議的很多要求,反而是二進制格式是C語言原生支持,也更方便移植
Python作為一個靈活的腳本語言,在運行環(huán)境搭建、編碼和調(diào)試方面都很便利,而且有豐富的第三方庫支持,通常只需要很少量的代碼就能夠輕松完成很多工作。
以下內(nèi)容分別從二進制文件和二進制數(shù)據(jù)流方面對Python中struct的用法進行說明
struct進行二進制操作的用法
struct是Python內(nèi)置的一個模塊,struct對數(shù)據(jù)格式的封裝能夠與C語言進行非常方便的適配,能夠完美的支持C語言原生的數(shù)據(jù)類型,以及指定字節(jié)順序。
以下表格是struct封裝格式與C語言數(shù)據(jù)類型的對應(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é)序,也是通過參數(shù)的方式指定的
Code | Meaning |
@ | Native order |
= | Native standard |
< | Little-endian |
> | Big-endian |
! | Network order |
怎么用?
struct提供了一些函數(shù)來封裝數(shù)據(jù)
pack(fmt, v1, v2, ...)
:按照給定的格式fmt,將數(shù)據(jù)v1和v2封裝rdata = unpack(fmt, data)
:按照給定的格式fmt,將源數(shù)據(jù)data解析為元組形式的rdatacalcsize(fmt)
:計算給定格式占用的內(nèi)存字節(jié)數(shù)pack_into(fmt, buffer, offset, v1, v2…)
:按照給定的格式fmt將數(shù)據(jù)以追加的方式封裝,寫入以offset開始的buffer中rdata = unpack_from(fmt, buffer, offset)
:按照給定的格式fmt解析以offset開始的緩沖區(qū)buffer,并返回解析結(jié)果
這些函數(shù)接口中的"fmt",是字符串的形式,其內(nèi)容就是上文中兩個表格的第一列。
例如:以下代碼將16進制字"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"可以是單獨某一種格式,也可以是多種格式的混合
例如:以下代碼將一個十六進制字節(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
二進制文件操作
利用struct進行二進制文件編輯,有以下關(guān)鍵點
- 使用ctypes.create_string_buffer創(chuàng)建buffer
- 使用pack_into進行追加封裝
- 文件打開使用'b'標(biāo)志表示以二進制方式寫入
以下例子利用struct封裝一個固定格式的數(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é)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python 實現(xiàn)使用dict 創(chuàng)建二維數(shù)據(jù)、DataFrame
下面小編就為大家分享一篇Python 實現(xiàn)使用dict 創(chuàng)建二維數(shù)據(jù)、DataFrame,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04Python Pandas pandas.read_sql_query函數(shù)實例用法分析
在本篇文章里小編給大家整理的是一篇關(guān)于Python Pandas pandas.read_sql_query函數(shù)實例用法分析內(nèi)容,有興趣的朋友們可以跟著學(xué)習(xí)下。2021-06-06