任意长度HEX文件的解析(Python实现)
1. 程序的功能
可解析大于64KB的HEX文件。限于时间条件,笔者只测试了两个样例,数据域大小分别为8332B和1.61MB
将解析得到的结果打印出来,包括:
- 起始地址
- 末尾地址
- 数据域尺寸(单位:字节)
保存数据域的内容为bin文件以便查验和后续处理
HEX文件格式的介绍可以参考CSDN上的这篇博文:https://blog.csdn.net/a1037488611/article/details/43340055
2. 程序解读
HEX文件解析,相关函数:hex2bin()
考虑到HEX文件通常不会很大,所以一次性读取全部内容,每1行为1个list,然后逐行分析
通过识别每1行的TT字段来执行相应的操作,流程图如下:
(1)数据打包
相关函数:char2hex()
通过readlines函数读取到的是一行行字符串,如第一行是“:020000040800F2”,此外末尾还包含一个不可见的回车符号。
我们要将数字字符转换成对应的整型数据以便运算和存储,行首的冒号和行尾的回车符仅用于格式判断,对于数据提取是无用的,故去除。
HEX格式定义中的记录域是以字节为最小单位的,所以还需要将两个整型字符拼成1个字节。
python提供的高阶函数map()和切片操作,极大地方便了我们对HEX文本进行信息提取和格式转换。
char2hex()函数最终返回的是如下形式的整型列表:
[0x02,0x00,0x00,0x04,0x08,0x00,0xF2]
(2)校验和
相关函数:checksum()
得到了1行数据的整型列表以后,最好对该行数据校验一下。
按照规定,每行记录的倒数第1个字节是校验和(Checksum),校验算法为:
0x100-((除了冒号以外的所有数据以字节为单位相加)%256)
笔者的代码实现如下:
sum = (0x100 - (reduce(lambda x,y:x+y,line[:-1]) % 256)) % 256
之所以最后要再对256取模,是考虑到算出来的结果正好为0x100的情况,在C语言里,不做这一步当然OK,因为C语言中数据类型有自身的固定长度,
超出长度的高位部分会自动截断,对于8位无符号数,0x100==0x00是成立的,而Python中的int是动态变化的,数据在内存中的组织不像C那样读编程者那么直观,不存在数据类型的定义,0x100和0x00当然就是不相等的。
(3)保存bin文件
相关函数:wr_bin()
执行完hex2bin()后,HEX文件中数据域全都按顺序,从低字节到高字节被写入到了1个整型list中,
但按照python的要求,将每个元素转换为byte类型才能写入到二进制文件中。代码如下:
bin_buf_byte = list(map(six.int2byte,bin_buf))
(4)运行结果分析
笔者所测试的两个样例hex都是由STM32的工程编译而来。
查看打印输出的运行结果:addr_start代表起始地址,内容比较。
hex和bin文件在对应位置上分别截取1行,还可以用鼠标在输出的bin文件上右击,查看“属性”-“常规”-“大小”来验证内容是否遗漏。
从以上两个列表可以直观地看出,程序正确地解析了hex文件的内容。
3. 代码实现
# -*- coding: utf-8 -*-
# File Name : hex2bin.py
# Version : V1.0.0001
# Date : 2019/03/06
# Author : qipeng_jiang
import os
import time
import six
from functools import reduce
# get '.hex' file name from current path
def getFileNamebyEX(path):
f_list = os.listdir(path)
for i in f_list:
filename = os.path.splitext(i)[0]
extname = os.path.splitext(i)[1]
if extname == '.hex':
print(" *Hex file : " + filename + extname+'*')
return i
bin_buf = []# raw data of binary is to be stored here
def hex2bin(hex_file_name,bin_file_name):
with open(hex_file_name,'r') as frd:
print(' *Hex file : \''+hex_file_name+'\''+' is opened*')
byte_num = 0
addr_end = 0
for line in frd.readlines():
#line.strip(); #cut off CR
if(line[0] == ':'):
if(line[7:9] == '04'): #Extended Linear Address Record
#print('Extended Linear Address Record');
line = char2hex(line)
if checksum(line) == 0: #checksum passed
addr_h = (line[4]<<24) +(line[5]<<16)
else:
print('checksum failed!'+str(list(map(hex,line))))
elif (line[7:9] == '00'): #Data Record
line = char2hex(line)
if checksum(line) == 0:
addr_l = (line[1]<<8) + line[2]
LL = line[0]
byte_num = byte_num + LL
for data in line[4:-1]:
bin_buf.append(data)
if LL!=0x10:
addr_end = addr_h + addr_l + LL - 1
else:
pass
else:
print('checksum failed!'+str(list(map(hex,line))))
elif(line[7:9] == '05'):
#print('Extended Segment Address Record');
line = char2hex(line)
if checksum(line) == 0: pass
else:
print('checksum failed!'+str(list(map(hex,line))))
elif(line[7:9] == '01'): #End of FileRecord
#print('End of FileRecord');
line = char2hex(line)
if checksum(line) == 0:
print(' *Hex file successed resolved*')
print(' *addr_start: 0x%08X' %(addr_end + 1 - len(bin_buf)))
print(' *addr_end : 0x%08X' %addr_end)
print(' *Total size : %d Bytes'%len(bin_buf))
else:
print('checksum failed!'+str(list(map(hex,line))))
else:
pass #don't care
else:
print('illegal format!')
# write data in bin_buf to '*.bin' file
def wr_bin(bin_buf):
bin_buf_byte = list(map(six.int2byte,bin_buf))
with open(bin_file_name,'wb') as fwrb:
print(' *Bin file \''+bin_file_name+'\''+' is opened for write*')
for data in bin_buf_byte:
fwrb.write(data)
print(' *Bin file is successfully written!*')
#one line string to hex-8 list,except ':' and CR
def char2hex(line):
line=list(map(ord,list(line)))
for num in range(len(line)):
if line[num]>=0x30 and line[num]<=0x39:
line[num] = line[num] - 0x30
elif line[num]>=0x41 and line[num]<=0x5A:
line[num] = line[num] - 55
else:
pass
line=line[1:-1] #delete CR and ':', in terms of byte
for i in range(0,len(line),2):
line[i] = line[i]*16 + line[i+1]
newline = line[::2]
return newline
#checksum calculation of every line
def checksum(line):
#considering if the checksum calculation result is 0x100
sum = (0x100 - (reduce(lambda x,y:x+y,line[:-1]) % 256)) % 256
if sum == line[-1]: #check if sum calculated is equal to checksum byte in hex file
return 0
else:
return 1
starttime = time.clock()
hex_file_name = getFileNamebyEX('.')
bin_file_name = hex_file_name[:-4]+'.bin'
hex2bin(hex_file_name,bin_file_name)
wr_bin(bin_buf)
endtime = time.clock()
print(' Time elapsed:' + str(endtime-starttime))4. 脚本运行
拷贝 .hex 文件到 python 脚本同级目录下,直接运行。打印log内容如下:
