Python如何利用struct进行二进制文件或数据流

 更新时间:2024年01月20日 10:49:29   作者:Wei.Studio  
这篇文章主要介绍了Python如何利用struct进行二进制文件或数据流问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

利用struct进行二进制文件或数据流操作

在实际工作场景中,特别是在嵌入式开发过程中,经常需要和二进制操作打交道。

这里举两个典型例子:

设备上的一些配置参数通常是以二进制的形式保存在Flash等非易失存储芯片中,而配置参数的格式与内容编辑工作通常是在PC上中完成,将生成的xxx.bin文件下载到设备并写入Flash中,设备启动后从Flash固定地址读取配置到内存中使用。如果能够在PC上以某种脚本的形式方便的对二进制文件的格式和内容进行编辑,或者将二进制文件解析为方便阅读的格式,可以提升很多工作效率,使用起来也非常方便

设备和管理端(上位机、服务器等)需要通过一定的接口进行数据交互,操作人员通过管理端访问和控制设备,例如使用UART进行串口通信,或者使用Ethernet接口进行UDP/TCP甚至应用层协议通信。无论使用什么接口,都需要对业务数据进行协议封装,使用二进制的数据流封装是一个非常普遍的选择,尤其是在资源非常紧张的嵌入式平台上,与使用字符串或JSON格式传输数据的协议(例如HTTP)相比,二进制占用的带宽和内存资源较少。另外,很多嵌入式平台上由于资源限制(内存、库函数支持等),并不能很好的集成复杂协议的很多要求,反而是二进制格式是C语言原生支持,也更方便移植

Python作为一个灵活的脚本语言,在运行环境搭建、编码和调试方面都很便利,而且有丰富的第三方库支持,通常只需要很少量的代码就能够轻松完成很多工作。

以下内容分别从二进制文件和二进制数据流方面对Python中struct的用法进行说明

struct进行二进制操作的用法

struct是Python内置的一个模块,struct对数据格式的封装能够与C语言进行非常方便的适配,能够完美的支持C语言原生的数据类型,以及指定字节顺序。

以下表格是struct封装格式与C语言数据类型的对应关系

struct支持的格式
FormatC TypePython TypeSize
xpad byteno value1
ccharstring of length 11
bsigned charinteger1
Bunsigned charinteger1
?_Boolbool1
hshortinteger2
Hunsigned shortinteger2
iintinteger4
Iunsigned intinteger4
llonginteger4
Lunsigned longinteger4
qlong long integer8
Qunsigned long longinteger8
ffloatfloat4
ddoublefloat8
schar[]string
pchar[]string
pvoid *integer

struct支持指定字节序,也是通过参数的方式指定的

struct字节序定义
CodeMeaning
@Native order
=Native standard
<Little-endian
>Big-endian
!Network order

怎么用?

struct提供了一些函数来封装数据

  • pack(fmt, v1, v2, ...):按照给定的格式fmt,将数据v1和v2封装
  • rdata = unpack(fmt, data):按照给定的格式fmt,将源数据data解析为元组形式的rdata
  • calcsize(fmt):计算给定格式占用的内存字节数
  • pack_into(fmt, buffer, offset, v1, v2…):按照给定的格式fmt将数据以追加的方式封装,写入以offset开始的buffer中
  • rdata = unpack_from(fmt, buffer, offset):按照给定的格式fmt解析以offset开始的缓冲区buffer,并返回解析结果

这些函数接口中的"fmt",是字符串的形式,其内容就是上文中两个表格的第一列。

例如:以下代码将16进制字"0x1516"分别以小端和大端形式封装为x1和x2

import struct
 
x1 = struct.pack('H', 0x1516)
x2 = struct.pack('>H', 0x1516)
print('x1:{} x2:{}'.format(x1, x2))
 
# running result:
# x1:b'\x15\x13' x2:b'\x13\x15'

参数中的"fmt"可以是单独某一种格式,也可以是多种格式的混合

例如:以下代码将一个十六进制字节"0xaa"和双字"0x11223344"合并封装为x,然后再解析为

import struct
 
x = struct.pack('BI', 0xaa, 0x11223344)
d1, d2 = struct.unpack('BI', x)
print('pack:{} unpack:{:#x} {:#x}'.format(x, d1, d2))
 
# running result:
# pack:b'\xaa\x00\x00\x00D3"\x11' unpack:0xaa 0x11223344

二进制文件操作

利用struct进行二进制文件编辑,有以下关键点

  • 使用ctypes.create_string_buffer创建buffer
  • 使用pack_into进行追加封装
  • 文件打开使用'b'标志表示以二进制方式写入

以下例子利用struct封装一个固定格式的数据到bin文件,并读出解析

# python3.7.6
 
import struct
from ctypes import create_string_buffer
 
 
"""
Python struct example
    package a format into binary finle
    
    format define:
    31               15              0
    +--------------------------------+
    |              mark              |
    +----------------+---------------+
    |     version    |    length     |
    +----------------+---------------+
    |            checksum            |
    +--------------------------------+
    |              data...           |
    +--------------------------------+
"""
 
 
def dump(mark, version, length, data, checksum):
    print('\n---- dump ----')
    print('mark:{:#X}'.format(mark))
    print('version:{}.{}'.format(version['major'], version['minor']))
    print('data length:{}'.format(length))
    print('checksum:{}'.format(checksum))
    print('data:{}'.format(data))
    print('--------------\n')
 
 
def save_bin(binary_file='test.bin', mark=0x5A5A5A5A, version={'major':1, 'minor':0}):
    
    offset = 0
    checksum = 0
    dataLength = 0
    data = [1,2,3,4,5,6,7,8]
    
    # create buffer
    buffer_size = 4+2+2+4+len(data)*4
    buffer = create_string_buffer(buffer_size)
    print('create buffer with {}byte'.format(buffer_size))
 
    # pack mark+version+length
    fmt = 'IBBH'
    dataLength = len(data)
    struct.pack_into(fmt, buffer, offset, mark, version['major'], version['minor'], dataLength)
    offset = offset + struct.calcsize(fmt)
 
    # pack data
    fmt = 'I'
    offset = offset + 4     # skip checksum
    for x in data:
        struct.pack_into(fmt, buffer, offset, x)
        offset = offset + struct.calcsize(fmt)
 
    # pack checksum
    fmt = 'I'
    offset = 8
    for x in buffer:
        d = x[0]
        checksum = checksum + d
    struct.pack_into(fmt, buffer, offset, checksum)
 
    # write to file
    f = open(binary_file, 'wb')
    f.write(buffer)
    f.close()
 
    dump(mark, version, dataLength, data, checksum)
 
    print('{}byte write to {}'.format(len(buffer), binary_file))
 
def read_bin(binary_file='test.bin'):
 
    offset = 0
    data = []
 
    # open and read file
    fmt = 'IBBHI'
    f = open(binary_file, 'rb')
    buffer = f.read()
    mark, major, minor, length, checksum = struct.unpack_from(fmt, buffer, 0)
    version = {'major':major, 'minor':minor}
 
    offset = struct.calcsize(fmt)
    for i in range(length-1):
        offset = offset + 4
        d = struct.unpack_from('I', buffer, offset)
        data.append(d[0])
 
    dump(mark, version, length, data, checksum)
    print('read {}byte from {}'.format(len(buffer), binary_file))
 
 
save_bin()
read_bin()
 
 
"""
running result:
create buffer with 44byte
---- dump ----
mark:0X5A5A5A5A
version:1.0
data length:8
checksum:405
data:[1, 2, 3, 4, 5, 6, 7, 8]
--------------
44byte write to test.bin
---- dump ----
mark:0X5A5A5A5A
version:1.0
data length:8
checksum:405
data:[2, 3, 4, 5, 6, 7, 8]
--------------
read 44byte from test.bin
"""

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Python 实现使用dict 创建二维数据、DataFrame

    Python 实现使用dict 创建二维数据、DataFrame

    下面小编就为大家分享一篇Python 实现使用dict 创建二维数据、DataFrame,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • Python中使用支持向量机SVM实践

    Python中使用支持向量机SVM实践

    这篇文章主要为大家详细介绍了Python中使用支持向量机SVM实践,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • python Dataframe字符串合并的操作方法

    python Dataframe字符串合并的操作方法

    Dataframe的字符串合并包括2种场景,1.合并df中其中几列字符串;2.将df中的字符串与外部字符串合并,本文主要介绍在Python下对Dataframe进行字符串合并操作的方法,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • Python快速优雅的批量修改Word文档样式

    Python快速优雅的批量修改Word文档样式

    本文主要将涉及os,glob,docx模块的综合应用,帮助大家快速批量修改Word文档样式实现办公自动化,感兴趣的朋友可以了解下
    2021-05-05
  • pygame实现贪吃蛇游戏

    pygame实现贪吃蛇游戏

    这篇文章主要为大家详细介绍了pygame实现贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 利用Python的Django框架中的ORM建立查询API

    利用Python的Django框架中的ORM建立查询API

    这篇文章主要介绍了利用Python的Django框架中的ORM建立查询API,对Managers和QuerySets进行了着重介绍,需要的朋友可以参考下
    2015-04-04
  • Windows直接运行python程序的两种方法

    Windows直接运行python程序的两种方法

    本文主要介绍了Windows直接运行python程序,包括新建bat脚本和新建vbs脚本,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • PyQt子线程处理业务事件的问题解决

    PyQt子线程处理业务事件的问题解决

    在PyQt中,主线程通常是指GUI主循环所在的线程,而子线程则是执行实际工作的线程,本文主要介绍了PyQt子线程处理业务事件的问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • Python Pandas pandas.read_sql_query函数实例用法分析

    Python Pandas pandas.read_sql_query函数实例用法分析

    在本篇文章里小编给大家整理的是一篇关于Python Pandas pandas.read_sql_query函数实例用法分析内容,有兴趣的朋友们可以跟着学习下。
    2021-06-06
  • Python Socket使用实例

    Python Socket使用实例

    这篇文章主要介绍了Python Socket使用实例,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12

最新评论