任意长度HEX文件的解析(Python实现)
johncheapon 于 2019年03月06日 发表在 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内容如下:

注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。 标签:python,Hex