介绍

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) # 等价于struct.pack("I 2s f", *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)
'''

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

  1. Python3标准库:struct二进制数据结构

    1. struct二进制数据结构 struct模块包括一些函数,这些函数可以完成字节串与原生Python数据类型(如数字和字符串)之间的转换. 1.1 函数与Struct类 struct提供了一组处理 ...

  2. Lua学习教程之 可变參数数据打包与解包

    利用table的pack与unpack进行数据打包与解包.測试代码例如以下: print("Test table.pack()----------------"); functio ...

  3. lambda表达式,filter,map,reduce,curry,打包与解包和

    当然是函数式那一套黑魔法啦,且听我细细道来. lambda表达式 也就是匿名函数. 用法:lambda 参数列表 : 返回值 例: +1函数 f=lambda x:x+1 max函数(条件语句的写法如 ...

  4. MPI 打包与解包函数 MPI_Pack(),MPI_Unpack()

    ▶ MPI 中与数据打包传输有关的几个函数 ● 函数 MPI_Pack() 与 MPI_Unpack() 的原型 MPI_METHOD MPI_Pack( _In_opt_ const void* i ...

  5. web socket RFC6455 frame 打包、解包

    #ifndef __APP_WEBSOCKET_FRAME_H__ #define __APP_WEBSOCKET_FRAME_H__ #include "memory.hpp" ...

  6. dpkg打包与解包

    1.打包 dpkg -b 2.解包 2.1 dpkg -X 解出包内容 2.2 dpkg -e 输出包控制信息

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

    tar命令 選項與參數: -c :建立打包檔案,可搭配 -v 來察看過程中被打包的檔名(filename) -t :察看打包檔案的內容含有哪些檔名,重點在察看『檔名』就是了: -x :解打包或解壓縮的 ...

  8. SummerVocation_Learning--java的自动打包与解包

    Auto Boxing: 自动将基础类型转换成对象(JDK1.5之后支持) Auto UnBoxing:自动将对象转换成基础类型 如 Map中的put方法,如果要传入键值对<a,1>,&l ...

  9. Linux下文件打包与解包

    打包(.tar):  tar -cvf Pro.tar /home/lin/Pro   #将/home/lin/Pro文件夹下的所有文件打包成Pro.tar 打解包(.tar.gz)  tar -cv ...

随机推荐

  1. java文件夹上传

    我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用. 首先我们需要了解的是上传文件三要素: 1.表单提交方式:post (get方式提交有大小 ...

  2. vue如何监听键盘事件中的按键?

    原文地址 背景 在一些搜索框中,我们往往需要监听键盘的按下(onkeydown)或抬起(onkeyup)事件以进行一些操作.在原生js或者jQuery中,我们需要判断e.keyCode的值来获取用户所 ...

  3. linux-32bit-内存管理

    一.进程与内存 进程如何使用内存? 毫无疑问所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用途不 ...

  4. Flume原理分析与使用案例

    1.flume的特点: flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统.支持在日志系统中定制各类数据发送方,用于收集数据:同时,Flume提供对数据进行简单处理,并写到各种数据 ...

  5. 迭代器iterator和traits编程技法

    前言 这段时间研读SGI-STL-v2.91源码,并提炼核心代码自己实现一遍,感觉受益颇深.觉得有必要写一些文章记录下学习过程的思考,行文旨在总结,会大量参考侯捷<STL源码剖析>的内容. ...

  6. setsockopt用法详解

    最近做的一个程序用到了IOCP通信模型,里面用到了setsockopt对套接字进行设置,看源代码的时候最setsockopt函数很不理解,看了msdn以后还是不太明白这个函数的用法,于是就到网上找了一 ...

  7. Docker 运行 MYSQL 数据库的总结

    公司里面要求做一个小demo 学习java相关的东西 然后使用了mysql数据库 很早之前做过mysql的容器化运行. 现在想想已经忘记的差不多了  所以这里总结一下 docker化运行mysql数据 ...

  8. gcc5+opencv4.0.1 "玄学"bug记录

    近期需要使用OpenCV中的gpu加速的一些函数,需要重新编译OpenCV库文件. 由于本机安装的cuda9.0对编译器gcc的版本有要求,平时常用的gcc7.0用不了,所以选用了gcc5.5 . O ...

  9. Java开源网页抓取工具httpClient以及jsoup

    网上看到不错的Java网页抓取工具和库 先记录一下 使用java开源工具httpClient及jsoup抓取解析网页数据

  10. LKM rootkit:Reptile学习

    简介 Reptile是github上一个很火的linux lkm rootkit,最近学习了一些linux rootkit的内容,在这里记录一下. 主要是分析reptile的实现 Reptile的使用 ...