python之struct模块处理二进制
嵌入式开发中,有时需要对二进制流文件进行读写操作,一种方法是将二进制流文件转换为c语言数组形式。
这样可以使用python的struct模块,python的struct模块可以方便的进行字节与二进制之间的相互转换。
1 struct模块常用的几个函数
函数 | 说明 |
---|---|
struct.pack(format, v1, v2, ...) | 按照给定的格式(fmt),把数据封装成字符串流(包括2进制流) |
struct.unpack(format, buffer) | 按照给定的格式(fmt)解析字节流,返回解析出来的元组tuple |
struct.unpack_from(format, buffer, offset=0) | 该函数可以将缓冲区buffer 中的内容在按照指定的格式fmt ,从偏移量为offset=numb 的位置开始进行读取,返回的是一个对应的元组tuple |
struct.calcsize(format) | 计算给定的格式(fmt)占用多少字节的内存 |
重要的是清楚格式(fmt)表达的含义,格式包含两个部分:
- 数据格式
- 数据字节序(大端or小端?)、对齐方式等。
数据格式如下:
Format | C Type | Python | 字节数 |
---|---|---|---|
x |
pad byte | no value | 1 |
c |
char |
string of length 1 | 1 |
b |
signed char |
integer | 1 |
B |
unsigned char |
integer | 1 |
? |
_Bool |
bool | 1 |
h |
short |
integer | 2 |
H |
unsigned short |
integer | 2 |
i |
int |
integer | 4 |
I |
unsigned int |
integer | 4 |
l |
long |
integer | 4 |
L |
unsigned long |
integer | 4 |
q |
long long |
integer | 8 |
Q |
unsigned long long |
integer | 8 |
n |
ssize_t |
integer | |
N |
size_t |
integer | |
e |
float | 2 | |
f |
float |
float | 4 |
d |
double |
float | 8 |
s |
char[] |
string | 1 |
p |
char[] |
string | 1 |
P |
void * |
integer |
字节序、对齐方式如下:
使用特殊字符来控制。
Character | Byte order | Size and alignment |
---|---|---|
@ |
native | native 凑够4个字节,等于c语言中的sizeof |
= |
native | standard 按原字节数 |
< |
little-endian | standard 按原字节数 |
> |
big-endian | standard 按原字节数 |
! |
network (= big-endian) | standard 按原字节数 |
细节可以查看官网,这里只展示一个例子。
3 将二进制数据转换成16进制数组
这里提供一个示例,来演示struct的使用。下面代码将二进制数据转换成16进制数组(代码参考2,有修改)。
# bin2hex.py
"""
将二进制数据的bin文件转换成C语言数组的形式并保存为 .c 源文件
"""
import sys, os
import struct
def bin2hex():
if len(sys.argv) != 2:
print("Usage: %s <bin_file>" % (sys.argv[0]))
return
filename = sys.argv[1]
# 读取二进制文件存放到list列表中
bindata = open(filename, 'rb').read()
offset = 0
fmt = '>B'
binListData = []
while offset < len(bindata):
unpackdata = struct.unpack_from(fmt, bindata, offset)
binListData.append("0x%.2x" % unpackdata[0])
offset += struct.calcsize(fmt)
## 将列表中的数据写入到 .c 源文件中
fileoutname = os.path.splitext(filename)[0] + '_arry.c'
print("write to C array file %s" % fileoutname)
with open(fileoutname, 'w') as fileOutput:
fileOutput.write("unsigned long hexDataLength = {};\n".format(len(binListData)))
fileOutput.write("unsigned char hexData[] = {\n")
for i in range(len(binListData) - 1):
if (i != 0) and (i % 16 == 0):
fileOutput.write("\n")
fileOutput.write(binListData[i] + ",")
fileOutput.write(binListData[len(binListData) - 1] + "\n};")
print("bin to C array success!")
if __name__ == '__main__':
bin2hex()
脚本使用方法:
python3 bin2hex.py <binfile>
生成 binfile_arry.c 16进制数组
16进制数组转二进制bit流
此外,提供一个16进制数组转为二进制bit流的脚本:
# hexarray2bin
"""
将数组文件转换成二进制流bin文件
"""
import sys, os
import struct
def hexarray2bin():
if len(sys.argv) != 2:
print("Usage: %s <hex_array_file>" % (sys.argv[0]))
return
filename = sys.argv[1]
# 读取数组存放到列表中
data_tmp = []
with open(filename, 'r') as fdata:
lines = fdata.readlines()
for line in lines:
for i in line.strip().split(','):
if i.isalnum():
data_tmp.append(int(i, 16))
## 将列表中的数据写入到 .bin 二进制流中
fileoutname = os.path.splitext(filename)[0] + '.bin'
print("write to bin file %s" % fileoutname)
fmt = ">%uI" % len(data_tmp)
with open(fileoutname, 'wb') as fileOutput:
bin_stream = struct.pack(fmt, *data_tmp)
fileOutput.write(bin_stream)
print("C array to bin success!")
if __name__ == '__main__':
hexarray2bin()
数组文件如下(手动去头去尾):
0xfcff0000,0xfcff0000,0x00000000,0x00000000,0x0000fcff,0x00000000,0xfcff0000,0x0000f8ff,
0xf8fff8ff,0xf8fff8ff,0xfcfffcff,0xfcfffcff,0xfcfffcff,0xfcfffcff,0xfcfffcff,0xfcfffcff,
...
然后调用脚本:
python3 hexarray2bin <hexarrayfile>
生成 hexarrayfile.bin 二进制bit流数组
参考: