Python如何讀寫二進(jìn)制數(shù)組數(shù)據(jù)
問題
你想讀寫一個二進(jìn)制數(shù)組的結(jié)構(gòu)化數(shù)據(jù)到Python元組中。
解決方案
可以使用 struct
模塊處理二進(jìn)制數(shù)據(jù)。 下面是一段示例代碼將一個Python元組列表寫入一個二進(jìn)制文件,并使用 struct
將每個元組編碼為一個結(jié)構(gòu)體。
from struct import Struct def write_records(records, format, f): ''' Write a sequence of tuples to a binary file of structures. ''' record_struct = Struct(format) for r in records: f.write(record_struct.pack(*r)) # Example if __name__ == '__main__': records = [ (1, 2.3, 4.5), (6, 7.8, 9.0), (12, 13.4, 56.7) ] with open('data.b', 'wb') as f: write_records(records, '<idd', f)
有很多種方法來讀取這個文件并返回一個元組列表。 首先,如果你打算以塊的形式增量讀取文件,你可以這樣做:
from struct import Struct def read_records(format, f): record_struct = Struct(format) chunks = iter(lambda: f.read(record_struct.size), b'') return (record_struct.unpack(chunk) for chunk in chunks) # Example if __name__ == '__main__': with open('data.b','rb') as f: for rec in read_records('<idd', f): # Process rec ...
如果你想將整個文件一次性讀取到一個字節(jié)字符串中,然后在分片解析。那么你可以這樣做:
from struct import Struct def unpack_records(format, data): record_struct = Struct(format) return (record_struct.unpack_from(data, offset) for offset in range(0, len(data), record_struct.size)) # Example if __name__ == '__main__': with open('data.b', 'rb') as f: data = f.read() for rec in unpack_records('<idd', data): # Process rec ...
兩種情況下的結(jié)果都是一個可返回用來創(chuàng)建該文件的原始元組的可迭代對象。
討論
對于需要編碼和解碼二進(jìn)制數(shù)據(jù)的程序而言,通常會使用 struct
模塊。 為了聲明一個新的結(jié)構(gòu)體,只需要像這樣創(chuàng)建一個 Struct
實(shí)例即可:
# Little endian 32-bit integer, two double precision floats record_struct = Struct('<idd')
結(jié)構(gòu)體通常會使用一些結(jié)構(gòu)碼值i, d, f等 [參考 Python文檔 ]。 這些代碼分別代表某個特定的二進(jìn)制數(shù)據(jù)類型如32位整數(shù),64位浮點(diǎn)數(shù),32位浮點(diǎn)數(shù)等。 第一個字符 < 指定了字節(jié)順序。在這個例子中,它表示”低位在前”。 更改這個字符為 > 表示高位在前,或者是 ! 表示網(wǎng)絡(luò)字節(jié)順序。
產(chǎn)生的 Struct
實(shí)例有很多屬性和方法用來操作相應(yīng)類型的結(jié)構(gòu)。 size
屬性包含了結(jié)構(gòu)的字節(jié)數(shù),這在I/O操作時非常有用。 pack()
和 unpack()
方法被用來打包和解包數(shù)據(jù)。比如:
>>> from struct import Struct >>> record_struct = Struct('<idd') >>> record_struct.size 20 >>> record_struct.pack(1, 2.0, 3.0) b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@' >>> record_struct.unpack(_) (1, 2.0, 3.0) >>>
有時候你還會看到 pack()
和 unpack()
操作以模塊級別函數(shù)被調(diào)用,類似下面這樣:
>>> import struct >>> struct.pack('<idd', 1, 2.0, 3.0) b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@' >>> struct.unpack('<idd', _) (1, 2.0, 3.0) >>>
這樣可以工作,但是感覺沒有實(shí)例方法那么優(yōu)雅,特別是在你代碼中同樣的結(jié)構(gòu)出現(xiàn)在多個地方的時候。 通過創(chuàng)建一個 Struct
實(shí)例,格式代碼只會指定一次并且所有的操作被集中處理。 這樣一來代碼維護(hù)就變得更加簡單了(因?yàn)槟阒恍枰淖円惶幋a即可)。
讀取二進(jìn)制結(jié)構(gòu)的代碼要用到一些非常有趣而優(yōu)美的編程技巧。 在函數(shù) read_records
中,iter()
被用來創(chuàng)建一個返回固定大小數(shù)據(jù)塊的迭代器。 這個迭代器會不斷的調(diào)用一個用戶提供的可調(diào)用對象(比如 lambda: f.read(record_struct.size)
), 直到它返回一個特殊的值(如b'‘),這時候迭代停止。例如:
>>> f = open('data.b', 'rb') >>> chunks = iter(lambda: f.read(20), b'') >>> chunks <callable_iterator object at 0x10069e6d0> >>> for chk in chunks: ... print(chk) ... b'\x01\x00\x00\x00ffffff\x02@\x00\x00\x00\x00\x00\x00\x12@' b'\x06\x00\x00\x00333333\x1f@\x00\x00\x00\x00\x00\x00"@' b'\x0c\x00\x00\x00\xcd\xcc\xcc\xcc\xcc\xcc*@\x9a\x99\x99\x99\x99YL@' >>>
如你所見,創(chuàng)建一個可迭代對象的一個原因是它能允許使用一個生成器推導(dǎo)來創(chuàng)建記錄。 如果你不使用這種技術(shù),那么代碼可能會像下面這樣:
def read_records(format, f): record_struct = Struct(format) while True: chk = f.read(record_struct.size) if chk == b'': break yield record_struct.unpack(chk)
在函數(shù) unpack_records()
中使用了另外一種方法 unpack_from()
。 unpack_from() 對于從一個大型二進(jìn)制數(shù)組中提取二進(jìn)制數(shù)據(jù)非常有用, 因?yàn)樗粫a(chǎn)生任何的臨時對象或者進(jìn)行內(nèi)存復(fù)制操作。 你只需要給它一個字節(jié)字符串(或數(shù)組)和一個字節(jié)偏移量,它會從那個位置開始直接解包數(shù)據(jù)。
如果你使用 unpack()
來代替 unpack_from()
, 你需要修改代碼來構(gòu)造大量的小的切片以及進(jìn)行偏移量的計(jì)算。比如:
def unpack_records(format, data): record_struct = Struct(format) return (record_struct.unpack(data[offset:offset + record_struct.size]) for offset in range(0, len(data), record_struct.size))
這種方案除了代碼看上去很復(fù)雜外,還得做很多額外的工作,因?yàn)樗鼒?zhí)行了大量的偏移量計(jì)算, 復(fù)制數(shù)據(jù)以及構(gòu)造小的切片對象。 如果你準(zhǔn)備從讀取到的一個大型字節(jié)字符串中解包大量的結(jié)構(gòu)體的話,unpack_from()
會表現(xiàn)的更出色。
在解包的時候,collections
模塊中的命名元組對象或許是你想要用到的。 它可以讓你給返回元組設(shè)置屬性名稱。例如:
from collections import namedtuple Record = namedtuple('Record', ['kind','x','y']) with open('data.p', 'rb') as f: records = (Record(*r) for r in read_records('<idd', f)) for r in records: print(r.kind, r.x, r.y)
如果你的程序需要處理大量的二進(jìn)制數(shù)據(jù),你最好使用 numpy
模塊。 例如,你可以將一個二進(jìn)制數(shù)據(jù)讀取到一個結(jié)構(gòu)化數(shù)組中而不是一個元組列表中。就像下面這樣:
>>> import numpy as np >>> f = open('data.b', 'rb') >>> records = np.fromfile(f, dtype='<i,<d,<d') >>> records array([(1, 2.3, 4.5), (6, 7.8, 9.0), (12, 13.4, 56.7)], dtype=[('f0', '<i4'), ('f1', '<f8'), ('f2', '<f8')]) >>> records[0] (1, 2.3, 4.5) >>> records[1] (6, 7.8, 9.0) >>>
最后提一點(diǎn),如果你需要從已知的文件格式(如圖片格式,圖形文件,HDF5等)中讀取二進(jìn)制數(shù)據(jù)時, 先檢查看看Python是不是已經(jīng)提供了現(xiàn)存的模塊。因?yàn)椴坏饺f不得已沒有必要去重復(fù)造輪子。
以上就是Python如何讀寫二進(jìn)制數(shù)組數(shù)據(jù)的詳細(xì)內(nèi)容,更多關(guān)于Python讀寫二進(jìn)制數(shù)組數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
- 關(guān)于Python字符編碼與二進(jìn)制不得不說的一些事
- python pip如何手動安裝二進(jìn)制包
- Python讀取二進(jìn)制文件代碼方法解析
- Python如何向SQLServer存儲二進(jìn)制圖片
- Python讀入mnist二進(jìn)制圖像文件并顯示實(shí)例
- python 讀取二進(jìn)制 顯示圖片案例
- python十進(jìn)制轉(zhuǎn)二進(jìn)制的詳解
- Python+OpenCV實(shí)現(xiàn)將圖像轉(zhuǎn)換為二進(jìn)制格式
- Python的bit_length函數(shù)來二進(jìn)制的位數(shù)方法
- Python二進(jìn)制文件讀取并轉(zhuǎn)換為浮點(diǎn)數(shù)詳解
- 將python圖片轉(zhuǎn)為二進(jìn)制文本的實(shí)例
- Python使用struct處理二進(jìn)制(pack和unpack用法)
相關(guān)文章
Python+Selenium實(shí)現(xiàn)短視頻自動上傳與發(fā)布的實(shí)踐
本文主要介紹了Python+Selenium實(shí)現(xiàn)短視頻自動上傳與發(fā)布的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Python基于matplotlib畫箱體圖檢驗(yàn)異常值操作示例【附xls數(shù)據(jù)文件下載】
這篇文章主要介紹了Python基于matplotlib畫箱體圖檢驗(yàn)異常值操作,涉及Python針對xls格式數(shù)據(jù)文件的讀取、matplotlib圖形繪制等相關(guān)操作技巧,并附帶xls數(shù)據(jù)文件供讀者下載參考,需要的朋友可以參考下2019-01-01python 處理telnet返回的More,以及get想要的那個參數(shù)方法
今天小編就為大家分享一篇python 處理telnet返回的More,以及get想要的那個參數(shù)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-02-02PYTHON實(shí)現(xiàn)SIGN簽名的過程解析
這篇文章主要介紹了PYTHON實(shí)現(xiàn)SIGN簽名的過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-10-10Python調(diào)用GPT3.5接口的最新方法實(shí)例詳解
這篇文章主要介紹了Python調(diào)用GPT3.5接口的最新方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03python動態(tài)規(guī)劃算法實(shí)例詳解
在本篇文章里小編給大家整理了關(guān)于python動態(tài)規(guī)劃算法實(shí)例內(nèi)容,有需要的朋友們可以參考學(xué)習(xí)下。2020-11-11Matplotlib多子圖使用一個圖例的實(shí)現(xiàn)
多子圖是Matplotlib中的一個功能,可以在同一圖形中創(chuàng)建多個子圖,本文主要介紹了Matplotlib多子圖使用一個圖例的實(shí)現(xiàn),感興趣的可以了解一下2023-08-08Python數(shù)據(jù)分析與機(jī)器學(xué)習(xí)在金融風(fēng)控中的應(yīng)用小結(jié)
本篇詳解了Python數(shù)據(jù)分析與機(jī)器學(xué)習(xí)在金融風(fēng)控中的應(yīng)用,包括數(shù)據(jù)收集與預(yù)處理、信用風(fēng)險評估模型、市場風(fēng)險管理模型和操作風(fēng)險監(jiān)控模型,感興趣的朋友跟隨小編一起看看吧2024-06-06