百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Python标准库之struct python标准库re

yuyutoo 2024-10-12 00:46 3 浏览 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的功能。

相关推荐

jQuery VS AngularJS 你更钟爱哪个?

在这一次的Web开发教程中,我会尽力解答有关于jQuery和AngularJS的两个非常常见的问题,即jQuery和AngularJS之间的区别是什么?也就是说jQueryVSAngularJS?...

Jquery实时校验,指定长度的「负小数」,小数位未满末尾补0

在可以输入【负小数】的输入框获取到焦点时,移除千位分隔符,在输入数据时,实时校验输入内容是否正确,失去焦点后,添加千位分隔符格式化数字。同时小数位未满时末尾补0。HTML代码...

如何在pbootCMS前台调用自定义表单?pbootCMS自定义调用代码示例

要在pbootCMS前台调用自定义表单,您需要在后台创建表单并为其添加字段,然后在前台模板文件中添加相关代码,如提交按钮和表单验证代码。您还可以自定义表单数据的存储位置、添加文件上传字段、日期选择器、...

编程技巧:Jquery实时验证,指定长度的「负小数」

为了保障【负小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【负小数】的方法。HTML代码<inputtype="text"class="forc...

一篇文章带你用jquery mobile设计颜色拾取器

【一、项目背景】现实生活中,我们经常会遇到配色的问题,这个时候去百度一下RGB表。而RGB表只提供相对于的颜色的RGB值而没有可以验证的模块。我们可以通过jquerymobile去设计颜色的拾取器...

编程技巧:Jquery实时验证,指定长度的「正小数」

为了保障【正小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【正小数】的方法。HTML做成方法<inputtype="text"class="fo...

jquery.validate检查数组全部验证

问题:html中有多个name[],每个参数都要进行验证是否为空,这个时候直接用required:true话,不能全部验证,只要这个数组中有一个有值就可以通过的。解决方法使用addmethod...

Vue进阶(幺叁肆):npm查看包版本信息

第一种方式npmviewjqueryversions这种方式可以查看npm服务器上所有的...

layui中使用lay-verify进行条件校验

一、layui的校验很简单,主要有以下步骤:1.在form表单内加上class="layui-form"2.在提交按钮上加上lay-submit3.在想要校验的标签,加上lay-...

jQuery是什么?如何使用? jquery是什么功能组件

jQuery于2006年1月由JohnResig在BarCampNYC首次发布。它目前由TimmyWilson领导,并由一组开发人员维护。jQuery是一个JavaScript库,它简化了客户...

django框架的表单form的理解和用法-9

表单呈现...

jquery对上传文件的检测判断 jquery实现文件上传

总体思路:在前端使用jquery对上传文件做部分初步的判断,验证通过的文件利用ajaxFileUpload上传到服务器端,并将文件的存储路径保存到数据库。<asp:FileUploadI...

Nodejs之MEAN栈开发(四)-- form验证及图片上传

这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能。开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/R...

大数据开发基础之JAVA jquery 大数据java实战

上一篇我们讲解了JAVAscript的基础知识、特点及基本语法以及组成及基本用途,本期就给大家带来了JAVAweb的第二个知识点jquery,大数据开发基础之JAVAjquery,这是本篇文章的主要...

推荐四个开源的jQuery可视化表单设计器

jquery开源在线表单拖拉设计器formBuilder(推荐)jQueryformBuilder是一个开源的WEB在线html表单设计器,开发人员可以通过拖拉实现一个可视化的表单。支持表单常用控件...

取消回复欢迎 发表评论: