(一)要做什麼
之前有這麼一個需求,是要把一個二進位制檔案裡面的資料,轉換成 C 程式碼裡面的陣列,可以看之前的一篇文章:
NUC980 執行 RT-Thread 驅動 SPI 介面 OLED 播放 badapple
於是用 python 把這個功能給做了出來,原理非常簡單,程式碼量也很小。
所處理的檔案大小如下,用一個編輯器以二進位制形式開啟的話,一行16位元組,一共 336448 行。
(二)實現功能
上程式碼,Show me your code.Talk is cheap.
import os
import time
start_time = time.time()
fileinfo = os.stat("badapple.bin")
line = fileinfo.st_size / 16
print(line)
首先匯入 os、time 模組,os 模組使用者獲取所處理檔案的大小,生成的標頭檔案裡面的陣列每 16 位元組一行,time 模組用於統計程式所使用時間,上述程式碼執行結果為:
336648 行,正好與之前看到的一致。
然後定義一個變數,用來儲存最終的資料:
target = "#ifndef __BADAPPLE_H__ \n#define __BADAPPLE_H__\n\n const uint8_t badapple[] = \n{\n"
然後開啟檔案 badapple.bin,以二進位制、只讀方式開啟,每次讀取一位元組,然後轉換成二進位制,與 0x 組成一個位元組資料,儲存到上述字串中,每處理完 16 位元組新增換行,處理完後關閉檔案,程式碼如下:
f = open("badapple.bin", "rb")
for l in range(int(line)):
for j in range(16):
data = f.read(1)
he = "0x" + data.hex()
target = target + he + ","
print(l)
target = target + "\n"
target = target + "}; \n\n#endif\n"
print(target)
f.close()
由於全部資料有 30 多萬行,不好測試,先只處理 badapple.bin 檔案中的前 15 行,來測試下程式碼有沒有問題,把上面程式碼中的第二行改為:
for l in range(15):
執行結果為:
看上去是可以的,然後實現把轉換出來的資料儲存到檔案中,並加上獲取執行改程式碼所花時間,程式碼如下:
file = open("badapple.h","w")
file.write(target)
file.close()
end_time = time.time()
print(end_time - start_time)
執行結果為:
所生成的檔案為:
從所生成的檔案來看,算是大功告成了,把所需處理行數改為實際檔案的行數,就完成了。
然而,我還是圖樣圖森破啊,我以為我在第二層,其實我在第五層。
第一次執行的時候,跑了幾十分鐘,停了,感覺所花時間大大久了,
出於對自己寫的程式碼負責的態度,第二天下午 3 點多的時候,再次把這程式碼跑了起來,可是到了6點多還是沒跑完,又中途結束了,
很好奇,究竟需要多長時間來跑,第三天,一早上就把程式碼跑了起來,結果是到了下午 6 點多,差不多 7 點才跑完,來看下執行的最後結果:
從數字來看,30545 秒,感覺也不是很大,可是轉換為小時的話:
30545 / 60 / 60 = 8.4
居然達到了 8.4 個小時,這是生成的檔案:
(三) 優化
要這麼久時間來跑肯定是不實際的,修改下程式碼,之前是一個位元組一個位元組從檔案裡面讀出來,這次改為每次從檔案中讀取 16 位元組,轉換,然後再讀取16位元組,知道結束,程式碼如下:
target = "#ifndef __BADAPPLE_H__ \n#define __BADAPPLE_H__\n\n const uint8_t badapple[] = \n{\n"
line = fileinfo.st_size / 16
print(line)
for l in range(int(line)):
data = f.read(16)
he = ""
for da in data:
he = he + hex(da) + ","
print(l)
target = target + he + "\n"
target = target + "}; \n\n#endif\n"
執行下,最後結果為:
2165 秒,大概是 36 分鐘,也還是有點長。
再改改,對比下這 2 中方法,第二種是每次讀出來的位元組數是第一種的16倍,速度明顯提升了,可以看出時間是損耗在從檔案讀取資料,那如果一次性把所用資料從檔案種讀取出來會不會更快呢?試了下,程式碼改為:
f = open("badapple.bin", "rb")
target = "#ifndef __BADAPPLE_H__ \n#define __BADAPPLE_H__\n\n const uint8_t badapple[] = \n{\n"
dat = f.read()
print(type(dat))
f.close()
whole = dat.__len__()
line = int(whole / 16)
for i in range(line):
temp = dat[i*16:i*16+16]
he = ""
for da in temp:
he = he + hex(da) + ","
target = target + he + "\n"
print(i)
target = target + "}; \n\n#endif\n"
運行了下,結果為:
2340 秒,居然比第二種方法還長。
還有什麼方法可以改進呢?現在時間估計是花在轉換上面,在轉換上有沒有改進方法,目前還沒找出來。
(四)做的更通用
那是不是說這段程式碼就完全沒用,非也非也,用來轉換小點的檔案還是可以的,這不止可以轉換二進位制檔案,還可以轉換其他任何檔案,比如圖片、字型檔等,以下是轉換一張圖片執行的結果:
為了更通用,修了下程式碼,如下:
執行的時候需要傳個引數,這個引數就是要處理的檔案,最後生成的標頭檔案、標頭檔案裡陣列命名都是根據處理的檔案的名字來設定的,執行過程中還顯示處理進度,最後顯示處理所花時間。比如把上述程式碼儲存到檔案 hex2header.py,比如要轉換的檔案為 test.pdf,用法為:
python hex2header.py test.pdf
實際執行為:
這示例轉換了一個 323K 大小的 pdf 檔案,每行 16 位元組的話,大概 20653 行,花了6.4 秒的時間。
轉載請註明出處:https://www.cnblogs.com/halin/