2013-12-16 121 views
2

我想追加一個我計算的crc到現有的二進制文件。用Python追加數據字節到二進制文件

例如,crc是0x55667788。

我想追加0x55,0x66,0x77和0x88到文件的末尾。

例如,如果我在HexEdit中打開文件,文件 的最後四個字節將顯示0x55667788。

這裏是我到目前爲止的代碼:

fileopen = askopenfilename() 
filename = open(fileopen, 'rb+') 
filedata = filename.read() 
filecrc32 = hex(binascii.crc32(filedata)) 
filename.seek(0,2) 
filename.write(filecrc32) 
filename.close() 

我得到以下錯誤:

File "C:\Users\cjackel\openfile.py", line 9, in <module> 
filename.write(filecrc32) 
TypeError: 'str' does not support the buffer interface 

有什麼建議?

+0

你打開文件*二進制*模式,但正在嘗試寫入*文本*字符串。您需要首先爲字符串選擇編碼,或者編寫其他二進制數據。 – millimoose

回答

4

hex函數返回一個字符串。在這種情況下,你已經有了一個代表你的4個字節數10個十六進制字符的字符串,像這樣:

'0x55667788' 

在Python 2.x中,你將被允許這種不正確的數據寫入到一個二進制文件(它將顯示爲10個字節30 78 35 35 36 36 37 37 38 38而不是所需的四個字節,55 66 77 88)。 Python 3.x更智能,並且只允許您將bytes(或bytearray或類似文件)寫入二進制文件,而不是str


這裏你想要的不是十六進制字符串,而是實際的字節。

你描述你想要的字節的方式叫做big-endian order。在大多數電腦上,「本地」訂單是相反的,小端,這會給你0x88776655,而不是0x55667788

在Python 3.2+,以獲得最簡單的方法是int.to_bytes方法:

filecrc = binascii.crc32(filedata).to_bytes(4, byteorder='big', signed=False) 

(該signed=False是不是真的有必要,因爲它是默認的,但它是擺明的好方法。那你肯定處理一個32位無符號整數)

如果你堅持早期版本中,你可以使用struct模塊:

filecrc = struct.pack('>I', binascii.crc32(filedata)) 

>表示big-endian,I表示無符號的4字節整數。所以,這返回相同的東西。在任何一種情況下,你得到的是b'\x55\x66\x77\x88'(或者,Python將爲repr it,b'\Ufw\x88')。


的錯誤是有點神祕,因爲沒有新手將會有什麼想法,什麼「緩衝區接口」是(特別是因爲3.x的文檔調用它的Buffer Protocol,並且它只能記錄爲一部分CPython的C擴展API ...),但實際上這意味着您需要一個bytes-like object。通常,這個錯誤意味着你忘記了將字符串編碼爲UTF-8或其他編碼。但是當你試圖編寫實際的二進制數據而不是編碼文本時,這是一樣的錯誤。

+1

你可以在Python 3.2+中使用'binascii.crc32(filedata).to_bytes(4,byteorder ='big')'' – jfs

+0

@JFSebastian:好吧,我可能也會使用明確的'signed = False',但否則,是的,這簡單得多。我已經忘記了3.2和它。謝謝! – abarnert

+0

我同意。儘管'binascii.crc32()'被記錄爲返回無符號整數,'signed = False'是默認值,顯式的'signed = False'使得在6個月內更容易閱讀,然後你忘記了它。 – jfs

0

您需要序列化數據。序列化是從整個數字獲取相關字節的過程。在你的情況下,你的CRC是一個 4字節的數字。各個4個字節可以檢索到列表如下:

serialized_crc = [(filecrc32 >> 24) & 0xFF,(filecrc32 >> 16) & 0xFF, 
       (filecrc32 >> 8) & 0xFF,filecrc32 & 0xFF] 

的CRC可以通過轉換爲ByteArray如下面再寫入文件:

filename.write(bytearray(serialized_crc))