Python标准库之struct(python struct库)
off999 2024-10-25 13:46 30 浏览 0 评论
Python 为了保持语言的简洁,仅为用户提供了几种简单的数据结构:int, float, str, list, dict,tuple。不同于编译型语言 C/C++,在 Python 中,我们往往不需要关心不同类型的变量在解释器内部的实现方式。例如,对于一个长整形数据,我们在 Python 2 中可以直接写成 a=123456789012345L,而不用去考虑变量 a 占了几个字节。这种抽象的方式为程序的编写提供了足够的支持,但是在某些情况下(比如读写二进制文件,进行网络 Raw Socket 编程)的时候,我们需要一些其他模块来实现我们关于变量长度控制的需求。
struct 模块
当我们在 Python 中跟二进制数据打交道的时候,就要用到 struct 这个模块了。struct 模块为 Python 与 C 的混合编程,处理二进制文件以及进行网络协议交互提供了便利。理解这个模块主要需要理解三个函数:
struct.pack(fmt, v1, v2, ...) struct.unpack(fmt, string) struct.calcsize(fmt)
第一个函数 pack 负责将不同的变量打包在一起,成为一个字节字符串,即类似于 C 语言中的字节流。第二个函数 unpack 将字节字符串解包成为变量。第三个函数 calsize 计算按照格式 fmt 打包的结果有多少个字节。这里打包格式 fmt 确定了将变量按照什么方式打包成字节流,其包含了一系列的格式字符串。
关于格式字符串
在Python手册中,给出了C语言中常用类型与Python类型对应的格式符:
struct.pack(fmt, v1, v2, ...)
Return a string containing the values v1, v2, ... packed according to the given format. The arguments must match the values required by the format exactly.
struct.pack用于将Python的值根据格式符,转换为字符串,准确来说是Byte。这个地方我们之前有提过,Python3内的unicode和bytes,在Py3内文本总是Unicode,由str类型表示,二进制数据则由bytes类型表示。
Py2是没有Byte这么个东西的。参数fmt是格式字符串,v1, v2, ...表示要转换的python值。下面的例子将两个整数转换为字符串:
import struct
a = 20
b = 400
byte = struct.pack("ii", a, b) #转换后的str相当于其他语言中的字节流(字节数组),可以在网络上传输
big = struct.pack(">ii", a, b) #大端保存
small = struct.pack("<ii", a, b) #小端保存
print(byte)
# >>>:b'\x14\x00\x00\x00\x90\x01\x00\x00'
print(big)
# >>>:b'\x00\x00\x00\x14\x00\x00\x01\x90'
print(small)
# >>>:b'\x14\x00\x00\x00\x90\x01\x00\x00'
print (byte[0],byte[4])
# >>>:b'\x14\x00\x00\x00\x90\x01\x00\x00'
格式符"i"表示转换为int,'ii'表示有两个int变量。进行转换后的结果长度为8个字节(int类型占用4个字节,两个int为8个字节)可以看到输出的结果是乱码,因为结果是二进制数据,所以显示为乱码。可以使用python的内置函数repr来获取可识别的字符串 ,以上问题在Python3中不会出现了其中十六进制的0x00000014, 0x00000190分别表示20和400。
上一段代码最后那个很有意思诶,竟然是默认采用小端。
大端存储和小端存储
小端:较高的有效字节存放在较高的存储器地址,较低的有效字节存放在较低的存储器地址。
大端:较高的有效字节存放在较低的存储器地址,较低的有效字节存放在较高的存储器地址。
如果将一个16位的整数0x1234存放到一个短整型变量(short)中。这个短整型变量在内存中的存储在大小端模式由下表所示。
地址偏移 大端模式 小端模式 0x00 12(OP0) 34(OP1) 0x01 34(OP1) 12(OP0)
采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。
struct.unpack(fmt, buffer)
Unpack from the buffer buffer (presumably packed by pack(fmt, ...)) according to the format string fmt. The result is a tuple even if it contains exactly one item. The buffer’s size in bytes must match the size required by the format, as reflected by calcsize().
struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个tuple。
import struct
a = struct.pack("2I3sI", 12, 34, "abc", 56)
b = struct.unpack("2I3sI", a)
print b
## 输出 (12, 34, 'abc', 56)
struct.calcsize(fmt)
Return the size of the struct (and hence of the bytes object produced by pack(fmt, ...)) corresponding to the format string fmt.
struct.calcsize用于计算格式字符串所对应的结果的长度,如:struct.calcsize('ii'),返回8。因为两个int类型所占用的长度是8个字节。
使用 struct 打包定长结构
一般而言,在使用 struct 的时候,要打包的数据都是定长的。定长的数据代表你需要明确给出要打包的或者解包的数据长度,否则打包解包函数将会出错。下面用例子说明什么是定长打包:
import struct
a = struct.pack("2I3sI", 12, 34, "abc", 56)
b = struct.unpack("2I3sI", a)
print b
## 输出 (12, 34, 'abc', 56)
上面的代码将两个整数 12 和 34,一个字符串 “abc” 和一个整数 56 一起打包成为一个字节字符流,然后再解包。其中打包格式中明确指出了打包的长度:"2I" 表明起始是两个unsigned int,"3s" 表明长度为4的字符串,最后一个 "I" 表示最后紧跟一个 unsigned int。所以上面的打印 b 输出结果是:(12, 34, ‘abc’, 56)。
我们可以调用 calcsize() 来计算 "2I3sI" 这个模式占用的字节数:
print struct.calcsize("2I3sI")
## 输出 16
可以看到上面的三个整型加一个 3 字符的字符串一共占用了 16 个字节。为什么会是 16 个字节呢?不应该是 15 个字节吗?其实,在 struct 的打包过程中,根据特定类型的要求,必须进行字节对齐。由于默认 unsigned int 型占用四个字节,因此要在字符串的位置进行4字节对齐,因此即使是 3 个字符的字符串也要占用 4 个字节。
再看一下不需要字节对齐的模式:
print struct.calcsize("2Is")
## 输出 9
由于单字符出现在两个整型之后,不需要进行字节对齐,所以输出结果是 9.
需要指出的是,对于 unpack 而言,只要 fmt 对应的字节数和字节字符串 string 的字节数一致,就可以成功的进行解析,否则 unpack 函数将抛出异常。例如我们也可以使用如下的 fmt 解析出 a:
c = struct.unpack("2I2sI", a)
print struct.calcsize("2I2sI")
print c
## 输出 16 (12, 34, 'ab', 56)
可以看到这里 unpack 解析出了字符串的前两个字符,没有产生任何问题。
struct 处理不定长数据
我们看到了在使用 pack 和 unpack 的过程中,我们需要明确的指出打包模式中每个位置的长度。比如格式 "2I3sI" 就明确指出了整型的个数和字符串的个数。有时候,我们还可能会需要处理变长的打包数据。
变长字符串的打包
例如我们在程序中可能会得到一个字符串 s,这个 s 没有一个固定的长度,所以我们每次打包的时候都需要将 s 的长度也打包到一起,这样我们才能进行正确的解包。其实,这种情况在处理网络数据包中非常常见。在使用网络编程的时候,我们可能利用报文的第一个字段记录报文的长度。每次读取报文的时候,我们先读取报文的第一个字段,获取其长度之后在处理报文内容。
我们可以采用两种方式处理这种情况:
s = bytes(s, 'utf-8') # Or other appropriate encoding
struct.pack("I%ds" % (len(s),), len(s), s)
或者
struct.pack("I", len(s)) + s
第一种方式先将报文转变成为字节码,然后获取字节码的长度,将长度嵌入到打包之后的报文中去。可以看到格式字符串中的 "I" 就用来记录报文的长度。第二种方式是直接将字符串的长度打包成字节字符串,再跟原始字符串做一个连接操作。
变长字符串的解包
根据上面的打包方式,我们可以轻松的解开打包串:
int_size = struct.calcsize("I")
(i,), data = struct.unpack("I", data[:int_size]), data[int_size:]
data_content = data[i:]
由于报文的长度 len(s) 我们使用定长的整型 "I" 进行了打包,所以解包的时候我们可以先将报文长度获取出来,之后再根据报文长度读取报文内容。
实现简单的自定义协议数据传输
工作曾曾遇到过一个需要后端服务生成多整图片,返回给前端js使用, 第一方案是使用base64把图片二进制文件转换成字符串,打包返回给前端使用,私有协议分三段如下:
例如:3L80205_768037_128848base64strs
可以端通过解析返回的数据头信息,获取图片数据,根据长度读取二进制长度生成多张图片,但在实际应用中发现,经过base64编码的二进制文件,文件大小增加了30%-50%,造成了不必要的网络开销,后经过优化,直接使用二进制传输文件,核心代码如下:
以上就是对使用struct进行数据打包,实现了简易版的ziplist的功能。
相关推荐
- 现在哪个浏览器好用(现在哪种浏览器好用)
-
一、谷歌浏览器谷歌浏览器是公认最好用的,这个可以从市场占有率看出端倪,超过三分之二的用户使用谷歌浏览器。Chrome浏览器以简洁快速著称,不管是普通用户还是开发人员,chrome浏览器都是首选。Chr...
- win10系统还是win7系统好(是win10好还是win7好)
-
就我个人觉得win7系统和win10系统都挺好用的,主要看个人习惯,win10系统也推出了3年了,相信很多用户也已经尝试过win10系统,操作上没有什么很大的区别,就是界面有些不同,这就看个人喜欢了。...
- 重装系统u盘制作教程(重装系统u盘怎么制作)
-
以下是重装电脑系统的一般步骤:在正常可用的电脑上下载并安装一个制作启动U盘的工具,例如Rufus、WinToUSB等。使用该工具将操作系统安装文件写入U盘,并设置U盘为启动盘。将U盘插入需要重装系统的...
- 从win8装回win7系统(win8.1怎么装回win7)
-
重装电脑,而且您的【电脑系统没有完全崩溃】,那么可以用【【【硬盘安装系统的方法】】】来装系统,无需借助光盘或者u盘,简单快速。电脑硬盘安装系统的方法如下:1.到WIN7旗舰版基地去http://ww...
- 查看电脑硬件信息的4个技巧
-
1、点击下面任务栏中的windows图标或按键盘windows键打开开始菜单。2、在开始菜单中找到设置或settings,通常是右上角的齿轮按钮。3、然后在设置界面中找到system选项,点击进入。4...
- 老式电脑的显卡在哪里(老式电脑主机显卡在哪)
-
十年前的老电脑没必要了,换新吧,估计接口什么也都有限制了我看了下你这个是品牌电脑,代工板的应该是没有显卡插槽的,有的话也是AGP的,可以去淘片AGPATI9000系列的卡或者6200660...
- word产品密钥2016(word产品密匙2016)
-
word2016激活密钥有两种类型:永久激活码和KMS期限激活密钥。其中,永久激活密钥可以使用批量授权版永久激活密钥进行激活,如所示;而KMS期限激活密钥需要使用KMS客户端密钥进行激活,如所示。另外...
- 如何选择电脑配置(如何选择电脑配置的高低)
-
选择电脑配置时,首先要考虑自己的需求和预算。确定用途(如游戏、办公、设计等),然后选择适合的处理器、内存、显卡、存储和显示器等。处理器要根据性能和功耗进行选择,内存要足够大以支持多任务处理,显卡要根据...
- win10下方任务栏无反应(win10下方任务栏消失)
-
按ctrl+alt+delete键win10系统电脑中,按键盘上的ctrl+alt+delete键2点击任务管理器点击出现窗口选项中的“任务管理器”3点击重新启动打开管理器后,右击Windows资源管...
- win11找不到wifi网络(win11找不到wifi网络图标)
-
原因:可能是缺少了无线网卡驱动。解决方法:1、打开开始菜单,在上方搜索并进入“设备管理器”。2、接着展开网络适配器,找到无线网卡设备,右键选中,点击“更新驱动程序”。3、最后选择“自动搜索驱动程序”,...
- 激活office2013密钥匙(office 2013 激活密钥)
-
这是KMS激活,180天以后需要重新激活。安装在其他路径的话改第二行,第二行的作用就是进入Office15所在目录,所以实际路径是什么就写什么。第三行是KMS服务器IP地址,你写的这个是俄罗斯服务器,...
- 电脑版本低如何更新为最新版本
-
首先说一点:2002年的电脑了,2002年的主板和现在出的部分硬件配不上,如果换主板的话就基本等于重新配置一台新的了;第二点就是非要硬性升级的话很多以前的硬件都不出产了,价格较高,真心不如重新弄一台新...
- 修理电脑(修理电脑一般多少钱)
-
故障一:电脑蓝屏电脑蓝屏算是常见的故障了,其中主要有四个原因。1.内存条接触不良打开机箱将内存条固定,同时对电脑进行除尘操作,这样的做法能够更好的保证电脑运行。若电脑仍然出现蓝屏,则可能是内存条的问题...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
