利用Python中的內(nèi)置open函數(shù)讀取二進(jìn)制文件
在python中讀取一個(gè)文本文件相信大家都比較熟悉了,但如果我們遇到一個(gè)二進(jìn)制文件要讀取怎么辦呢?我們嘗試使用 Python 中的內(nèi)置 open 函數(shù)使用默認(rèn)讀取模式讀取 zip 文件,抱歉,我們將收到錯(cuò)誤消息:
>>> with open("exercises.zip") as zip_file:
... contents = zip_file.read()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/usr/lib/python3.10/codecs.py", line 322, in de
code
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8e in position 11: invalid sta
rt byte我們收到一個(gè)錯(cuò)誤,是因?yàn)?zip 文件不是文本文件,它們是二進(jìn)制文件。
要從二進(jìn)制文件中讀取,我們需要使用模式 rb 而不是默認(rèn)模式 rt 打開它:
>>> with open("exercises.zip", mode="rb") as zip_file:
... contents = zip_file.read()當(dāng)從二進(jìn)制文件中讀取時(shí),我們不會(huì)得到字符串。將返回一個(gè)字節(jié)對(duì)象,也稱為字節(jié)字符串:
>>> with open("exercises.zip", mode="rb") as zip_file:
... contents = zip_file.read()
...
>>> type(contents)
<class 'bytes'>
>>> contents[:20]
b'PK\x03\x04\n\x00\x00\x00\x00\x00Y\x8e\x84T\x00\x00\x00\x00\x00\x00'字節(jié)字符串中沒有字符:它們中有字節(jié)。
除非我們理解它們的含義,否則文件中的字節(jié)對(duì)我們沒有多大幫助。
使用庫來讀取二進(jìn)制文件
處理二進(jìn)制文件時(shí),你通常會(huì)使用和知道如何處理正在使用的特定類型文件的庫(內(nèi)置 Python 庫或第三方庫)。該庫將完成將文件中的字節(jié)解碼為更易于使用的工作。
例如,Python 的 ZipFile 模塊可以幫助我們讀取 zip 文件中的數(shù)據(jù):
>>> from zipfile import ZipFile
>>>
>>> with ZipFile("exercises.zip") as zip_file:
... test_file = zip_file.read("exercises/test.py").decode("utf-8")
...
>>> test_file[:30]
'#!/usr/bin/env python3\nfrom __'如果有人已經(jīng)完成了這項(xiàng)工作,最好避免實(shí)現(xiàn)自己的字節(jié)檢查或字節(jié)操作邏輯。
在 Python 中以字節(jié)級(jí)別工作
有時(shí)你會(huì)使用或被要求直接在字節(jié)級(jí)別工作的庫或 API。在這種情況下,你需要至少需要對(duì)二進(jìn)制文件和字節(jié)字符串有一點(diǎn)了解。
例如,假設(shè)我們要計(jì)算給定文件的 sha256 校驗(yàn)和。
在這里,我們有一個(gè)名為 get_sha256_hash 的函數(shù)來執(zhí)行此操作:
import hashlib
def get_sha256_hash(filename):
with open(filename, mode="rb") as f:
return hashlib.sha256(f.read()).hexdigest()此函數(shù)讀取此文件中的所有二進(jìn)制數(shù)據(jù)。我們正在讀取字節(jié),因?yàn)?Python 的 hashlib 模塊要求我們使用字節(jié)。hashlib 模塊在底層工作:它使用字節(jié)而不是字符串。
因此,我們傳入文件中的所有字節(jié)以獲取哈希對(duì)象,然后對(duì)該哈希對(duì)象調(diào)用 hexdigest 方法以獲取表示該文件的 SHA-256 校驗(yàn)和的十六進(jìn)制字符串:
>>> get_sha256_hash("exercises.zip")
'9e98242a21760945ec815668fc79d8621fa15dd23659ea29be2c5949153fe96d'此功能運(yùn)行良好,但使用此功能讀取非常大的文件可能會(huì)出現(xiàn)問題。
分塊讀取二進(jìn)制文件
我們的 get_sha256_hash 函數(shù)一次將整個(gè)文件讀入內(nèi)存。一個(gè)非常大的文件可能會(huì)占用大量內(nèi)存。
對(duì)于文本文件,解決此問題的常用方法是逐行讀取文件。但是二進(jìn)制文件不一定有行!但是,我們可以嘗試逐塊讀取。
首先,我們將從文件中讀取一個(gè) 8 KB 的塊:
import hashlib
def get_sha256_hash(filename, buffer_size=2**10*8):
file_hash = hashlib.sha256()
with open(filename, mode="rb") as f:
chunk = f.read(buffer_size)我們首先創(chuàng)建一個(gè)新的哈希對(duì)象,然后讀取一個(gè) 8 KB 的塊(通過將字節(jié)數(shù)傳遞給我們的文件對(duì)象的 read 方法)。
現(xiàn)在我們需要文件的其余部分。所以我們將循環(huán):
import hashlib
def get_sha256_hash(filename, buffer_size=2**10*8):
file_hash = hashlib.sha256()
with open(filename, mode="rb") as f:
chunk = f.read(buffer_size)
while chunk:
file_hash.update(chunk)
chunk = f.read(buffer_size)
return file_hash.hexdigest()我們重復(fù)讀取一個(gè)塊,更新我們的哈希對(duì)象,然后讀取另一個(gè)塊。
只要我們不在文件的末尾,我們就會(huì)在讀取時(shí)返回一個(gè)真實(shí)的塊。
但是當(dāng)我們?cè)谖募淖詈笞x取時(shí),我們會(huì)得到一個(gè)空字節(jié)字符串??兆止?jié)字符串(如空字符串)是錯(cuò)誤的,因此在文件末尾我們將跳出循環(huán)。然后我們將像以前一樣返回十六進(jìn)制摘要。
>>> get_sha256_hash("exercises.zip")
'9e98242a21760945ec815668fc79d8621fa15dd23659ea29be2c5949153fe96d'但是,我們現(xiàn)在不是將整個(gè)文件讀入內(nèi)存,而是逐塊讀取文件。
使用賦值表達(dá)式
在逐塊讀取文件時(shí),通常會(huì)看到使用的賦值表達(dá)式(通過 Python 的海象運(yùn)算符):
import hashlib
def get_sha256_hash(filename, buffer_size=2**10*8):
file_hash = hashlib.sha256()
with open(filename, mode="rb") as f:
while chunk := f.read(buffer_size):
file_hash.update(chunk)
return file_hash.hexdigest()在 while 循環(huán)中重復(fù)讀取數(shù)據(jù)是賦值表達(dá)式的一個(gè)很好的用例。它可能看起來有點(diǎn)奇怪,但它確實(shí)為我們節(jié)省了幾行代碼。
注意:海象運(yùn)算符是在 Python 3.8 中添加的。
最后總結(jié)下,當(dāng)你在 Python 中讀取二進(jìn)制文件時(shí),你會(huì)得到字節(jié),當(dāng)你讀取一個(gè)大型二進(jìn)制文件時(shí),你需要逐塊讀取它,當(dāng)然如果可以最好避免自己讀取二進(jìn)制文件,有第三方庫可以使用第三方庫來處理。
相關(guān)文章
python-序列解包(對(duì)可迭代元素的快速取值方法)
今天小編就為大家分享一篇python-序列解包(對(duì)可迭代元素的快速取值方法),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-08-08
python3 pathlib庫Path類方法總結(jié)
這篇文章主要介紹了python3 pathlib庫Path類方法總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
python通過Seq2Seq實(shí)現(xiàn)閑聊機(jī)器人
這篇文章主要介紹了python通過Seq2Seq實(shí)現(xiàn)閑聊機(jī)器人,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python的小伙伴們有很好的幫助,需要的朋友可以參考下2021-04-04
Python批量查詢關(guān)鍵詞微信指數(shù)實(shí)例方法
在本篇文章中小編給大家整理的是關(guān)于Python批量查詢關(guān)鍵詞微信指數(shù)實(shí)例方法以及相關(guān)代碼,需要的朋友們可以跟著學(xué)習(xí)下。2019-06-06

