struct--二进制数据结构的打包与解包

介绍

struct模块包括一些函数,这些函数可以完成字节串与原生Python数据类型(如数字和字符串)之间的转换

函数与Struct类

struct提供了一组处理结构值的模块级函数,另外还有一个Struct类,这与处理正则表达式的compile类似。

类比正则:re.match(pattern, text) 使用这种模块级别的函数时,会先将pattern进行编译转换,这个转换是耗费资源的。因此可以先对pattern进行一个编译,comp = re.compile(pattern),comp.match(text).这样的话就只需要转换一次,struct也是类似的情况,所以创建一个Struct实例并在这个实例上调用方法时(不使用模块级函数)只完成一次转换,这会更高效

打包与解包

import struct

'''
Struct支持使用格式指示符将数据打包(packing)为字符串,另外支持从字符串解包(unpacking)数据。
格式指示符由表示数据类型的字符和可选的数量及字节序(endianness)指示符构成。
要全面了解目前可支持的数据结构,可以参考标准库文档
'''
import binascii

# values包含一个整型或长整型,一个两字节字符串,以及一个浮点数。
values = (1, "ab".encode("utf-8"), 2.7)
# 格式指示符中包含的空格用来分割类型指示符,并且在编译格式时会被忽略
# 使用Struct定义格式,I:整型,2s:两个字节的字符,f:浮点数,之间使用空格分隔
# 表示打包的数据有三个,分别是整型,两个字节的字符,以及一个浮点
s = struct.Struct("I 2s f")
# 使用s.pack函数进行打包,将values打开传进去
packed_data = s.pack(*values)

# s:Struct对象
print(s)  # <Struct object at 0x0000000002924458>
# 原始数据values
print("原始数据:", values)  # 原始数据: (1, b'ab', 2.7)
# 打印一下我们的格式,也就是我们传进去的格式
print("格式化字符:", s.format)  # 格式化字符: I 2s f
# 查看所用的字节
print("使用:", s.size, "bytes")  # 使用: 12 bytes
# 查看打包之后的结果
print("打包后的结果:", packed_data)  # 打包后的结果: b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@'
print("将打包的结果进行转换:", binascii.hexlify(packed_data))  # 将打包的结果进行转换: b'0100000061620000cdcc2c40'

# 我们传入values,通过s.pack()得到packed_data,那么我们传入packed_data,可不可以调用一个函数反过来得到values呢?
# 答案是可以的,可以使用s.unpack()
# 值得一提的是,这个binascii.hexlify,还有一个相反的函数叫做binascii.unhexlify
print(packed_data)  # b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@'
print(binascii.hexlify(packed_data))  # b'0100000061620000cdcc2c40'
print(binascii.unhexlify(binascii.hexlify(packed_data)))  # b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@'

# 使用s.unpack()
print(s.unpack(packed_data))  # (1, b'ab', 2.700000047683716)
'''
可以看到还是可以转回来的,注意这个浮点数啊,这是计算机的存储误差,任何语言都是有这个问题的。
'''

字节序

import struct

'''
默认地,值会使用原生C库的字节序(endianness)来编码。
只需在格式中提供一个显示的字节序指令,就可以很容易地覆盖这个默认选择
'''
import binascii

values = (1, "ab".encode("utf-8"), 2.7)
print("original values:", values)

endianness = [
    ("@", "native, native"),
    ("=", "native, standard"),
    ("<", "little-endian"),
    (">", "big-endian"),
    ("!", "network")
]

for code, name in endianness:
    s = struct.Struct(code + " I 2s f")
    packed_data = s.pack(*values)
    print("*"*20)
    print("Format string: ", s.format, "for", name)
    print("uses: ", s.size, "bytes")
    print("hex packed data:", binascii.hexlify(packed_data))
    print("unpacked data", s.unpack(packed_data))

# @:原生顺序
# =:原生标准
# <:小端
# >:大端
# !:网络顺序

'''
original values: (1, b'ab', 2.7)
********************
Format string:  @ I 2s f for native, native
uses:  12 bytes
hex packed data: b'0100000061620000cdcc2c40'
unpacked data (1, b'ab', 2.700000047683716)
********************
Format string:  = I 2s f for native, standard
uses:  10 bytes
hex packed data: b'010000006162cdcc2c40'
unpacked data (1, b'ab', 2.700000047683716)
********************
Format string:  < I 2s f for little-endian
uses:  10 bytes
hex packed data: b'010000006162cdcc2c40'
unpacked data (1, b'ab', 2.700000047683716)
********************
Format string:  > I 2s f for big-endian
uses:  10 bytes
hex packed data: b'000000016162402ccccd'
unpacked data (1, b'ab', 2.700000047683716)
********************
Format string:  ! I 2s f for network
uses:  10 bytes
hex packed data: b'000000016162402ccccd'
unpacked data (1, b'ab', 2.700000047683716)
'''

缓冲区

import struct

'''
通常在强调性能的情况下,或者向扩展模块传入、传出数据时,才会处理二进制打包数据。
通过避免为每个打包结构分配一个新缓冲区所带来的开销,这些情况可以得到优化。
pack_into和unpack_from方法支持直接写入预分配的缓冲区
'''
import binascii
import ctypes
import array

s = struct.Struct("I 2s f")
values = (1, "ab".encode("utf-8"), 2.7)
print("original:", values)
print("---------------")
print("ctypes string buffer")

# 创建一个string缓存,大小为s.size
b = ctypes.create_string_buffer(s.size)
print("before:", b.raw, binascii.hexlify(b.raw))

# s.pack表示打包,s.pack_into表示打包到什么地方,至于第二个参数0表示偏移量,表示从头开始
s.pack_into(b, 0, *values)
print("after:", b.raw, binascii.hexlify(b.raw))

# s.unpack表示解包,s.unpack_from表示从什么地方解包,参数0表示偏移量,表示从头开始
print("unpacked:", s.unpack_from(b, 0))

print("---------------")
print("array")

a = array.array("b", b"\0"*s.size)
print("before:", a, binascii.hexlify(a))
s.pack_into(a, 0, *values)
print("after:", binascii.hexlify(a))
print("unpacked:", s.unpack_from(a, 0))

'''
original: (1, b'ab', 2.7)
---------------
ctypes string buffer
before: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'000000000000000000000000'
after: b'\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@' b'0100000061620000cdcc2c40'
unpacked: (1, b'ab', 2.700000047683716)
---------------
array
before: array('b', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) b'000000000000000000000000'
after: b'0100000061620000cdcc2c40'
unpacked: (1, b'ab', 2.700000047683716)
'''

原文地址:https://www.cnblogs.com/traditional/p/11870207.html

时间: 2024-10-12 07:52:38

struct--二进制数据结构的打包与解包的相关文章

web socket RFC6455 frame 打包、解包

#ifndef __APP_WEBSOCKET_FRAME_H__ #define __APP_WEBSOCKET_FRAME_H__ #include "memory.hpp" class buffer; struct websocket_frame { websocket_frame(); ~websocket_frame(); static const unsigned int fix_min_len = 2; static const unsigned int fix_mask

Lua学习教程之 可变参数数据打包与解包

利用table的pack与unpack进行数据打包与解包,测试代码如下: print("Test table.pack()----------------"); function printTable ( t ) if type(t) == "table" then for k,v in pairs(t) do printTable(v); end else print(t); end end local packData =function( ... ) loca

python之打包,解包

#coding:utf-8 #字符串,列表,元组打包与解包 aString = 'abc' aList = [1, 2, 3] aTuple = 'a', 'A', 1 print('Unpacking string......') first, second, third = aString print("string values:", first, second, third) print('\nUnpacking list......') first, second, thir

Linux学习笔记(二十)文件压缩 zip压缩、tar打包、打包、解包

一.zip压缩 首先安装zip与unzipyum install -y zip/unzip zip 1.txt.zip 1.txt 压缩文件1.txt,压缩文件名称为1.txt.zip zip -r 123.zip 123/ 压缩文件夹123/ 指定名称123.zip unzip 1.txt.zip zip压缩文件并不会删除源文件,解压时会提示是否覆盖已存在的文件 unzip 123.zip -d /root/456/ 解压123.zip文件到/root/456/ 目录下 unzip -l 12

CentOS7 tar打包工具 打包,解包,打包压缩,打包解压缩

tar命令 選項與參數: -c :建立打包檔案,可搭配 -v 來察看過程中被打包的檔名(filename) -t :察看打包檔案的內容含有哪些檔名,重點在察看『檔名』就是了: -x :解打包或解壓縮的功能,可以搭配 -C (大寫) 在特定目錄解開 特別留意的是, -c, -t, -x 不可同時出現在一串指令列中. -z :透過 gzip 的支援進行壓縮/解壓縮:此時檔名最好為 *.tar.gz -j :透過 bzip2 的支援進行壓縮/解壓縮:此時檔名最好為 *.tar.bz2 -J :透過 x

linux下zip命令打包与解包

inux zip命令的基本用法是: zip [参数] [打包后的文件名] [打包的目录路径] linux zip命令参数列表: -a 将文件转成ASCII模式-F 尝试修复损坏的压缩文件-h 显示帮助界面-m 将文件压缩之后,删除源文件-n 特定字符串 不压缩具有特定字尾字符串的文件-o 将压缩文件内的所有文件的最新变动时间设为压缩时候的时间-q 安静模式,在压缩的时候不显示指令的执行过程-r 将指定的目录下的所有子目录以及文件一起处理-S 包含系统文件和隐含文件(S是大写)-t 日期 把压缩文

(转)基于RTP的H264视频数据打包解包类

最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包.解包的文档和代码.功夫不负有心人,找到不少有价值的文档和代码.参考这些资料,写了H264 RTP打包类.解包类,实现了单个NAL单元包和FU_A分片单元包.对于丢包处理,采用简单的策略:丢弃随后的所有数据包,直到收到关键帧.测试效果还不错,代码贴上来,若能为同道中人借鉴一二,足矣.两个类的使用说明如下(省略了错误处理过程): DWORD H264SSRC ; CH264_RTP_PACK pack ( H264S

java基础类型包装类与自动打包解包

一基础类型包装类 基础数据类型存放在内存的栈区域,可以通过包装类将基础数据类型转换为引用数据类型,即存储在堆与栈中. 基础数据类型一共有8种,相对应的包装类也有8种.分别是Byte,Short,Integer,Long,Float,Double,Character,Boolean.类 包装类位于java.Lang包中. 1 public class Test1 { 2 3 public static void main(String[] args) { 4 // TODO Auto-genera

【Java】Java包装类,Java的自动打包(装箱)与解包(拆箱)

包装类 Java中一切都是对象,所以很多操作都是针对对象的,Java会把常用的数据类型,自动包装成相应的类的对象进行操作. jdk1.5之后支持自动的打包与解包 常用的数据类型对应的包装类 装箱 装箱就是把基本数据类型变为相应类的对象 ArrayList list = new ArrayList();//list的各种操作都是针对对象的 list.add(5);//5本来是int类型的,现在自动包装成了Integer类的对象 拆箱 拆箱就是把相对应类的对象变为相应的基本数据类型 list.get