Python标准库笔记struct模块的使用


Posted in Python onFebruary 22, 2018

最近在学习python网络编程这一块,在写简单的socket通信代码时,遇到了struct这个模块的使用,当时不太清楚这到底有和作用,后来查阅了相关资料大概了解了,在这里做一下简单的总结。

了解c语言的人,一定会知道struct结构体在c语言中的作用,它定义了一种结构,里面包含不同类型的数据(int,char,bool等等),方便对某一结构对象进行处理。而在网络通信当中,大多传递的数据是以二进制流(binary data)存在的。当传递字符串时,不必担心太多的问题,而当传递诸如int、char之类的基本数据的时候,就需要有一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输,而接收端也应该可以通过某种机制进行解包还原出原始的结构体数据。python中的struct模块就提供了这样的机制,该模块的主要作用就是对python基本类型值与用python字符串格式表示的C struct类型间的转化(This module performs conversions between Python values and C structs represented as Python strings.)。stuct模块提供了很简单的几个函数,下面写几个例子。

该模块作用是完成Python数值和C语言结构体的Python字符串形式间的转换。这可以用于处理存储在文件中或从网络连接中存储的二进制数据,以及其他数据源。

用途: 在Python基本数据类型和二进制数据之间进行转换

struct模块提供了用于在字节字符串和Python原生数据类型之间转换函数,比如数字和字符串。

模块函数和Struct类

它除了提供一个Struct类之外,还有许多模块级的函数用于处理结构化的值。这里有个格式符(Format specifiers)的概念,是指从字符串格式转换为已编译的表示形式,类似于正则表达式的处理方式。通常实例化Struct类,调用类方法来完成转换,比直接调用模块函数有效的多。下面的例子都是使用Struct类。

Packing(打包)和Unpacking(解包)

Struct支持将数据packing(打包)成字符串,并能从字符串中逆向unpacking(解压)出数据。

在本例中,格式指定器(specifier)需要一个整型或长整型,一个两个字节的string,和一个浮点数。格式符中的空格用于分隔各个指示器(indicators),在编译格式时会被忽略。

import struct

import binascii

values = (1, 'ab'.encode('utf-8'), 2.7)
s = struct.Struct('I 2s f')
packed_data = s.pack(*values)

print('原始值:', values)
print('格式符:', s.format)
print('占用字节:', s.size)
print('打包结果:', binascii.hexlify(packed_data))

# output
原始值: (1, b'ab', 2.7)
格式符: b'I 2s f'
占用字节: 12
打包结果: b'0100000061620000cdcc2c40'

这个示例将打包的值转换为十六进制字节序列,用binascii.hexlify()方法打印出来。

使用unpack()方法解包。

import struct
import binascii

packed_data = binascii.unhexlify(b'0100000061620000cdcc2c40')

s = struct.Struct('I 2s f')
unpacked_data = s.unpack(packed_data)
print('解包结果:', unpacked_data)

# output
解包结果: (1, b'ab', 2.700000047683716)

将打包的值传给unpack(),基本上返回相同的值(浮点数会有差异)。

字节顺序/大小/对齐

默认情况下,pack是使用本地C库的字节顺序来编码的。格式化字符串的第一个字符可以用来表示填充数据的字节顺序、大小和对齐方式,如下表所描述的:

Character Byte order Size Alignment
@ 本地 本地 本地
= 本地 standard none
little-endian(小字节序) standard none
> big-endian(大字节序) standard none
! network (= big-endian) standard none

如果格式符中没有设置这些,那么默认将使用 @。

本地字节顺序是指字节顺序是由当前主机系统决定。比如:Intel x86和AMD64(x86-64)使用小字节序; Motorola 68000和 PowerPC G5使用大字节序。ARM和Intel安腾支持切换字节序。可以使用sys.byteorder查看当前系统的字节顺序。

本地大小(Size)和对齐(Alignment)是由c编译器的sizeof表达式确定的。它与本地字节顺序对应。

标准大小由格式符确定,下面会讲各个格式的标准大小。

示例:

import struct
import binascii

values = (1, 'ab'.encode('utf-8'), 2.7)
print('原始值 : ', 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()
 print('格式符 : ', s.format, 'for', name)
 print('占用字节: ', s.size)
 print('打包结果: ', binascii.hexlify(packed_data))
 print('解包结果: ', s.unpack(packed_data))

# output
原始值  :  (1, b'ab', 2.7)

格式符  :  b'@ I 2s f' for native, native
占用字节:  12
打包结果:  b'0100000061620000cdcc2c40'
解包结果:  (1, b'ab', 2.700000047683716)

格式符  :  b'= I 2s f' for native, standard
占用字节:  10
打包结果:  b'010000006162cdcc2c40'
解包结果:  (1, b'ab', 2.700000047683716)

格式符  :  b'< I 2s f' for little-endian
占用字节:  10
打包结果:  b'010000006162cdcc2c40'
解包结果:  (1, b'ab', 2.700000047683716)

格式符  :  b'> I 2s f' for big-endian
占用字节:  10
打包结果:  b'000000016162402ccccd'
解包结果:  (1, b'ab', 2.700000047683716)

格式符  :  b'! I 2s f' for network
占用字节:  10
打包结果:  b'000000016162402ccccd'
解包结果:  (1, b'ab', 2.700000047683716)

格式符

格式符对照表如下:

Format C Type Python type Standard size Notes
x pad byte no value
c char bytes of length 1 1
b signed char integer 1 (1),(3)
B unsigned char integer 1 (3)
? _Bool bool 1 (1)
h short integer 2 (3)
H unsigned short integer 2 (3)
i int integer 4 (3)
I unsigned int integer 4 (3)
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long integer 8 (2), (3)
n ssize_t integer (4)
N size_t integer (4)
f float float 4 (5)
d double float 8 (5)
s char[] bytes
p char[] bytes
P void * integer (6)

缓冲区

将数据打包成二进制通常是用在对性能要求很高的场景。

在这类场景中可以通过避免为每个打包结构分配新缓冲区的开销来优化。

pack_into()和unpack_from()方法支持直接写入预先分配的缓冲区。

import array
import binascii
import ctypes
import struct

s = struct.Struct('I 2s f')
values = (1, 'ab'.encode('utf-8'), 2.7)
print('原始值:', values)

print()
print('使用ctypes模块string buffer')

b = ctypes.create_string_buffer(s.size)
print('原始buffer :', binascii.hexlify(b.raw))
s.pack_into(b, 0, *values)
print('打包结果写入 :', binascii.hexlify(b.raw))
print('解包  :', s.unpack_from(b, 0))

print()
print('使用array模块')

a = array.array('b', b'\0' * s.size)
print('原始值 :', binascii.hexlify(a))
s.pack_into(a, 0, *values)
print('打包写入 :', binascii.hexlify(a))
print('解包  :', s.unpack_from(a, 0))

# output
原始值: (1, b'ab', 2.7)

使用ctypes模块string buffer
原始buffer  : b'000000000000000000000000'
打包结果写入 : b'0100000061620000cdcc2c40'
解包        : (1, b'ab', 2.700000047683716)

使用array模块
原始值   : b'000000000000000000000000'
打包写入 : b'0100000061620000cdcc2c40'
解包     : (1, b'ab', 2.700000047683716)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python调用shell的方法
Nov 20 Python
Python实现压缩和解压缩ZIP文件的方法分析
Sep 28 Python
selenium+python实现自动登录脚本
Apr 22 Python
Python类的继承用法示例
Jan 31 Python
在Python中使用Neo4j的方法
Mar 14 Python
Python及Pycharm安装方法图文教程
Aug 05 Python
python GUI库图形界面开发之PyQt5树形结构控件QTreeWidget详细使用方法与实例
Mar 02 Python
Jupyter notebook设置背景主题,字体大小及自动补全代码的操作
Apr 13 Python
Python参数传递对象的引用原理解析
May 22 Python
关于keras.layers.Conv1D的kernel_size参数使用介绍
May 22 Python
python中selenium库的基本使用详解
Jul 31 Python
如何在windows下安装配置python工具Ulipad
Oct 27 Python
python实现手机通讯录搜索功能
Feb 22 #Python
Python实现通讯录功能
Feb 22 #Python
Python SQLite3简介
Feb 22 #Python
Python Web程序部署到Ubuntu服务器上的方法
Feb 22 #Python
Python中 传递值 和 传递引用 的区别解析
Feb 22 #Python
centos 安装python3.6环境并配置虚拟环境的详细教程
Feb 22 #Python
windows下安装python的C扩展编译环境(解决Unable to find vcvarsall.bat)
Feb 21 #Python
You might like
利用 window_onload 实现select默认选择
2006/10/09 PHP
php循环检测目录是否存在并创建(循环创建目录)
2011/01/06 PHP
PHP学习笔记 用户注册模块用户类以及验证码类
2011/09/20 PHP
浅析PHP substr,mb_substr以及mb_strcut的区别和用法
2013/06/21 PHP
php json_encode()函数返回json数据实例代码
2014/10/10 PHP
使用XHGui来测试PHP性能的教程
2015/07/03 PHP
Yii2.0实现的批量更新及批量插入功能示例
2019/01/29 PHP
PHP生成zip压缩包的常用方法示例
2019/08/22 PHP
TP框架实现上传一张图片和批量上传图片的方法分析
2020/04/23 PHP
JavaScript DOM学习第一章 W3C DOM简介
2010/02/19 Javascript
jquery+css+ul模拟列表菜单具体实现思路
2013/04/15 Javascript
js操作label给label赋值及取label的值示例
2013/11/07 Javascript
jquery简单实现滚动条下拉DIV固定在头部不动
2013/11/25 Javascript
JS实现点击链接取消跳转效果的方法
2014/01/24 Javascript
jquery解析xml字符串示例分享
2014/03/25 Javascript
javascript学习总结之js使用技巧
2015/09/02 Javascript
jQuery代码实现实时获取时间
2017/01/29 Javascript
使用bootstrap-paginator.js 分页来进行ajax 异步分页请求示例
2017/03/09 Javascript
vue项目总结之文件夹结构配置详解
2017/12/13 Javascript
vue实现直播间点赞飘心效果的示例代码
2019/09/20 Javascript
JS实现网站吸顶条
2020/01/08 Javascript
详解Vue的组件中data选项为什么必须是函数
2020/08/17 Javascript
如何阻止移动端浏览器点击图片浏览
2020/08/29 Javascript
在vant 中使用cell组件 定义图标该图片和位置操作
2020/11/02 Javascript
python入门之语句(if语句、while语句、for语句)
2015/01/19 Python
Python处理PDF及生成多层PDF实例代码
2017/04/24 Python
Python实现Pig Latin小游戏实例代码
2018/02/02 Python
python爬虫之自动登录与验证码识别
2020/06/15 Python
Python 中 function(#) (X)格式 和 (#)在Python3.*中的注意事项
2018/11/30 Python
Python并发:多线程与多进程的详解
2019/01/24 Python
Python生命游戏实现原理及过程解析(附源代码)
2019/08/01 Python
乌克兰巴士票购买网站:inBus
2021/03/12 全球购物
MySQL面试题目集锦
2016/04/14 面试题
特岗教师个人总结
2015/02/10 职场文书
postgresql使用filter进行多维度聚合的解决方法
2021/07/16 PostgreSQL
mysql中DCL常用的用户和权限控制
2022/03/31 MySQL