任意长度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内容如下: