python之struct详解

2018-05-23 18:20:29 醉小义 阅读数 20115更多

分类专栏: python
 
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。

用处

  1. 按照指定格式将Python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时先将int转化为字节流,然后再发送;
  2. 按照指定格式将字节流转换为Python指定的数据类型;
  3. 处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件;
  4. 处理c语言中的结构体;

struct模块中的函数

函数 return explain
pack(fmt,v1,v2…) string 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回.
pack_into(fmt,buffer,offset,v1,v2…) None 按照给定的格式(fmt),将数据转换成字符串(字节流),并将字节流写入以offset开始的buffer中.(buffer为可写的缓冲区,可用array模块)
unpack(fmt,v1,v2…..) tuple 按照给定的格式(fmt)解析字节流,并返回解析结果
pack_from(fmt,buffer,offset) tuple 按照给定的格式(fmt)解析以offset开始的缓冲区,并返回解析结果
calcsize(fmt) size of fmt 计算给定的格式(fmt)占用多少字节的内存,注意对齐方式

格式化字符串

当打包或者解包的时,需要按照特定的方式来打包或者解包.该方式就是格式化字符串,它指定了数据类型,除此之外,还有用于控制字节顺序、大小和对齐方式的特殊字符.

对齐方式

为了同c中的结构体交换数据,还要考虑c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.定义如下

Character Byte order Size Alignment
@(默认) 本机 本机 本机,凑够4字节
= 本机 标准 none,按原字节数
< 小端 标准 none,按原字节数
> 大端 标准 none,按原字节数
! network(大端) 标准 none,按原字节数

如果不懂大小端,见大小端参考网址.

格式符

格式符 C语言类型 Python类型 Standard size
x pad byte(填充字节) no value  
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I(大写的i) unsigned int integer 4
l(小写的L) long integer 4
L unsigned long long 4
q long long long 8
Q unsigned long long long 8
f float float 4
d double float 8
s char[] string  
p char[] string  
P void * long  

注- -!

  1. _Bool在C99中定义,如果没有这个类型,则将这个类型视为char,一个字节;
  2. q和Q只适用于64位机器;
  3. 每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int;
  4. P用来转换一个指针,其长度和计算机相关;
  5. f和d的长度和计算机相关;

进制转化:

# 获取用户输入十进制数
dec = int(input("输入数字:")) print("十进制数为:", dec)
print("转换为二进制为:", bin(dec))
print("转换为八进制为:", oct(dec))
print("转换为十六进制为:", hex(dec))
  • 16进制转10进制: int('0x10', 16)  ==>  16

Python没有专门处理字节的数据类型。但由于b'str'可以表示字节,所以,字节数组=二进制str。而在C语言中,我们可以很方便地用struct、union来处理字节,以及字节和int,float的转换。

在Python中,比方说要把一个32位无符号整数变成字节,也就是4个长度的bytes,你得配合位运算符这么写:

  1.  
    >>> n = 10240099
  2.  
    >>> b1 = (n & 0xff000000) >> 24
  3.  
    >>> b2 = (n & 0xff0000) >> 16
  4.  
    >>> b3 = (n & 0xff00) >> 8
  5.  
    >>> b4 = n & 0xff
  6.  
    >>> bs = bytes([b1, b2, b3, b4])
  7.  
    >>> bs
  8.  
    b'\x00\x9c@c'

非常麻烦。如果换成浮点数就无能为力了。

好在Python提供了一个struct模块来解决bytes和其他二进制数据类型的转换。

structpack函数把任意数据类型变成bytes

  1.  
    >>> import struct
  2.  
    >>> struct.pack('>I', 10240099)
  3.  
    b'\x00\x9c@c'

pack的第一个参数是处理指令,'>I'的意思是:

>表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。

后面的参数个数要和处理指令一致。

struct

阅读: 58181

准确地讲,Python没有专门处理字节的数据类型。但由于b'str'可以表示字节,所以,字节数组=二进制str。而在C语言中,我们可以很方便地用struct、union来处理字节,以及字节和int,float的转换。

在Python中,比方说要把一个32位无符号整数变成字节,也就是4个长度的bytes,你得配合位运算符这么写:

  1.  
    >>> n = 10240099
  2.  
    >>> b1 = (n & 0xff000000) >> 24
  3.  
    >>> b2 = (n & 0xff0000) >> 16
  4.  
    >>> b3 = (n & 0xff00) >> 8
  5.  
    >>> b4 = n & 0xff
  6.  
    >>> bs = bytes([b1, b2, b3, b4])
  7.  
    >>> bs
  8.  
    b'\x00\x9c@c'

非常麻烦。如果换成浮点数就无能为力了。

好在Python提供了一个struct模块来解决bytes和其他二进制数据类型的转换。

structpack函数把任意数据类型变成bytes

  1.  
    >>> import struct
  2.  
    >>> struct.pack('>I', 10240099)
  3.  
    b'\x00\x9c@c'

pack的第一个参数是处理指令,'>I'的意思是:

>表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。

后面的参数个数要和处理指令一致。

unpackbytes变成相应的数据类型:

  1.  
    >>> struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')
  2.  
    (4042322160, 32896)

根据>IH的说明,后面的bytes依次变为I:4字节无符号整数和H:2字节无符号整数。

所以,尽管Python不适合编写底层操作字节流的代码,但在对性能要求不高的地方,利用struct就方便多了。

struct模块定义的数据类型可以参考Python官方文档:

https://docs.python.org/3/library/struct.html#format-characters

Windows的位图文件(.bmp)是一种非常简单的文件格式,我们来用struct分析一下。

首先找一个bmp文件,没有的话用“画图”画一个。

读入前30个字节来分析:

>>> s = b'\x42\x4d\x38\x8c\x0a\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\x68\x01\x00\x00\x01\x00\x18\x00'

BMP格式采用小端方式存储数据,文件头的结构按顺序如下:

两个字节:'BM'表示Windows位图,'BA'表示OS/2位图;一个4字节整数:表示位图大小;一个4字节整数:保留位,始终为0;一个4字节整数:实际图像的偏移量;一个4字节整数:Header的字节数;一个4字节整数:图像宽度;一个4字节整数:图像高度;一个2字节整数:始终为1;一个2字节整数:颜色数。

所以,组合起来用unpack读取:

  1.  
    >>> struct.unpack('<ccIIIIIIHH', s)
  2.  
    (b'B', b'M', 691256, 0, 54, 40, 640, 360, 1, 24)
  3.  
     

结果显示,b'B'b'M'说明是Windows位图,位图大小为640x360,颜色数为24。

请编写一个bmpinfo.py,可以检查任意文件是否是位图文件,如果是,打印出图片大小和颜色数。

  1.  
    # -*- coding: utf-8 -*-
  2.  
     
  3.  
    import base64,struct
  4.  
     
  5.  
    bmp_data = base64.b64decode('Qk1oAgAAAAAAADYAAAAoAAAAHAAAAAoAAAABABAAAAAAADICAAASCwAAEgsAAAAAAAAAAAAA/3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9/AHwAfAB8AHwAfAB8AHwAfP9//3//fwB8AHwAfAB8/3//f/9/AHwAfAB8AHz/f/9//3//f/9//38AfAB8AHwAfAB8AHwAfAB8AHz/f/9//38AfAB8/3//f/9//3//fwB8AHz/f/9//3//f/9//3//f/9/AHwAfP9//3//f/9/AHwAfP9//3//fwB8AHz/f/9//3//f/9/AHwAfP9//3//f/9//3//f/9//38AfAB8AHwAfAB8AHwAfP9//3//f/9/AHwAfP9//3//f/9//38AfAB8/3//f/9//3//f/9//3//fwB8AHwAfAB8AHwAfAB8/3//f/9//38AfAB8/3//f/9//3//fwB8AHz/f/9//3//f/9//3//f/9/AHwAfP9//3//f/9/AHwAfP9//3//fwB8AHz/f/9/AHz/f/9/AHwAfP9//38AfP9//3//f/9/AHwAfAB8AHwAfAB8AHwAfAB8/3//f/9/AHwAfP9//38AfAB8AHwAfAB8AHwAfAB8/3//f/9//38AfAB8AHwAfAB8AHwAfAB8/3//f/9/AHwAfAB8AHz/fwB8AHwAfAB8AHwAfAB8AHz/f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//38AAA==')
  6.  
     
  7.  
     
  8.  
    def bmp_info(data):
  9.  
     
  10.  
    str = struct.unpack('<ccIIIIIIHH',data[:30]) #bytes类也有切片方法
  11.  
     
  12.  
     
  13.  
    if str[0]==b'B' and str[1]==b'M':
  14.  
     
  15.  
    print("这是位图文件")
  16.  
     
  17.  
    return {
  18.  
    'width': str[-4],
  19.  
    'height': str[-3],
  20.  
    'color': str[-1]
  21.  
    }
  22.  
     
  23.  
    else:
  24.  
     
  25.  
    print("这不是位图文件")
  26.  
     
  27.  
     
  28.  
    if __name__ == '__main__':
  29.  
    bmp_info(bmp_data)
  30.  
    print('ok')
  31.  
     
  32.  
     
>>> from struct import *
>>> pack('hhl', 1, 2, 3)
b'\x00\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>> calcsize('hhl')
8

Unpacked fields can be named by assigning them to variables or by wrapping the result in a named tuple:

>>>

>>> record = b'raymond   \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record) >>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name=b'raymond ', serialnum=4658, school=264, gradelevel=8)

The ordering of format characters may have an impact on size since the padding needed to satisfy alignment requirements is different:

>>>

>>> pack('ci', b'*', 0x12131415)
b'*\x00\x00\x00\x12\x13\x14\x15'
>>> pack('ic', 0x12131415, b'*')
b'\x12\x13\x14\x15*'
>>> calcsize('ci')
8
>>> calcsize('ic')
5

The following format 'llh0l' specifies two pad bytes at the end, assuming longs are aligned on 4-byte boundaries:

>>>

>>> pack('llh0l', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'

示例

现在我们有了格式字符串,也知道了封装函数,那现在先通过一两个例子看一看。

例一:比如有一个报文头部在C语言中是这样定义的

struct header

{

unsigned short  usType;

char[4]               acTag;

unsigned int      uiVersion;

unsigned int      uiLength;

};

在C语言对将该结构体封装到一块缓存中是很简单的,可以使用memcpy()实现。在Python中,使用struct就需要这样:

str = struct.pack('B4sII', 0x04, 'aaaa', 0x01, 0x0e)

'B4sII'  ------   有一个unsigned short、char[4], 2个unsigned int。其中s之前的数字说明了字符串的大小 。

type, tag, version, length = struct.unpack('B4sll', str)

class struct.Struct(format)

返回一个struct对象(结构体,参考C)。

该对象可以根据格式化字符串的格式来读写二进制数据。

第一个参数(格式化字符串)可以指定字节的顺序。

默认是根据系统来确定,也提供自定义的方式,只需要在前面加上特定字符即可:

struct.Struct('>I4sf')

特定字符对照表附件有。

常见方法和属性:

方法

pack(v1, v2, )

返回一个字节流对象。

按照fmt(格式化字符串)的格式来打包参数v1,v2,...。

通俗的说就是:

首先将不同类型的数据对象放在一个“组”中(比如元组(1,'good',1.22)),

然后打包(“组”转换为字节流对象),最后再解包(将字节流对象转换为“组”)。

pack_into(buffer, offset, v1, v2, …)

根据格式字符串fmt包装值v1,v2,...,并将打包的字节写入从位置偏移开始的可写缓冲buffer。 请注意,offset是必需的参数。

unpack_from(buffer, offset=0)

根据格式字符串fmt,从位置偏移开始从缓冲区解包。 结果是一个元组,即使它只包含一个项目。 缓冲区的大小(以字节为单位,减去偏移量)必须至少为格式所需的大小,如calcsize()所反映的。

属性

format

格式化字符串。

size

结构体的大小。

实例:

1.通常的打包和解包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding: utf-8 -*-
"""
打包和解包
"""
import struct
import binascii
 
values = (1, b'good', 1.22) #查看格式化对照表可知,字符串必须为字节流类型。
s = struct.Struct('I4sf')
packed_data = s.pack(*values)
unpacked_data = s.unpack(packed_data)
  
print('Original values:', values)
print('Format string :', s.format)
print('Uses :', s.size, 'bytes')
print('Packed Value :', binascii.hexlify(packed_data))
print('Unpacked Type :', type(unpacked_data), ' Value:', unpacked_data)

结果:

Original values: (1, b'good', 1.22)
Format string : b'I4sf'
Uses : 12 bytes
Packed Value : b'01000000676f6f64f6289c3f'
Unpacked Type : <class 'tuple'> Value: (1, b'good', 1.2200000286102295)
[Finished in 0.1s]

说明:

首先将数据对象放在了一个元组中,然后创建一个Struct对象,并使用pack()方法打包该元组;最后解包返回该元组。

这里使用到了binascii.hexlify(data)函数。

binascii.hexlify(data)

返回字节流的十六进制字节流。

1
2
3
4
5
6
7
>>> a = 'hello'
>>> b = a.encode()
>>> b
b'hello'
>>> c = binascii.hexlify(b)
>>> c
b'68656c6c6f'

2.使用buffer来进行打包和解包

使用通常的方式来打包和解包会造成内存的浪费,所以python提供了buffer的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: utf-8 -*-
"""
通过buffer方式打包和解包
"""
import struct
import binascii
import ctypes
 
values = (1, b'good', 1.22) #查看格式化字符串可知,字符串必须为字节流类型。
s = struct.Struct('I4sf')
buff = ctypes.create_string_buffer(s.size)
packed_data = s.pack_into(buff,0,*values)
unpacked_data = s.unpack_from(buff,0)
  
print('Original values:', values)
print('Format string :', s.format)
print('buff :', buff)
print('Packed Value :', binascii.hexlify(buff))
print('Unpacked Type :', type(unpacked_data), ' Value:', unpacked_data)

结果:

Original values1: (1, b'good', 1.22)
Original values2: (b'hello', True)
buff : <ctypes.c_char_Array_18 object at 0x000000D5A5617348>
Packed Value : b'01000000676f6f64f6289c3f68656c6c6f01'
Unpacked Type : <class 'tuple'>  Value: (1, b'good', 1.2200000286102295)
Unpacked Type : <class 'tuple'>  Value: (b'hello', True)
[Finished in 0.1s]

说明:

针对buff对象进行打包和解包,避免了内存的浪费。

这里使用到了函数

ctypes.create_string_buffer(init_or_size,size = None)

创建可变字符缓冲区。
返回的对象是c_char的ctypes数组。

init_or_size必须是一个整数,它指定数组的大小,或者用于初始化数组项的字节对象。

3.使用buffer方式来打包多个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding: utf-8 -*-
"""
buffer方式打包和解包多个对象
"""
import struct
import binascii
import ctypes
 
values1 = (1, b'good', 1.22) #查看格式化字符串可知,字符串必须为字节流类型。
values2 = (b'hello',True)
s1 = struct.Struct('I4sf')
s2 = struct.Struct('5s?')
buff = ctypes.create_string_buffer(s1.size+s2.size)
packed_data_s1 = s1.pack_into(buff,0,*values1)
packed_data_s2 = s2.pack_into(buff,s1.size,*values2)
unpacked_data_s1 = s1.unpack_from(buff,0)
unpacked_data_s2 = s2.unpack_from(buff,s1.size)
  
print('Original values1:', values1)
print('Original values2:', values2)
print('buff :', buff)
print('Packed Value :', binascii.hexlify(buff))
print('Unpacked Type :', type(unpacked_data_s1), ' Value:', unpacked_data_s1)
print('Unpacked Type :', type(unpacked_data_s2), ' Value:', unpacked_data_s2)

结果:

Original values2: (b'hello', True)
buff : <ctypes.c_char_Array_18 object at 0x000000D5A5617348>
Packed Value : b'01000000676f6f64f6289c3f68656c6c6f01'
Unpacked Type : <class 'tuple'> Value: (1, b'good', 1.2200000286102295)
Unpacked Type : <class 'tuple'> Value: (b'hello', True)
[Finished in 0.1s]

python之struct详解的更多相关文章

  1. go语言之行--结构体(struct)详解、链表

    一.struct简介 go语言中没有像类的概念,但是可以通过结构体struct实现oop(面向对象编程).struct的成员(也叫属性或字段)可以是任何类型,如普通类型.复合类型.函数.map.int ...

  2. Python 字符串方法详解

    Python 字符串方法详解 本文最初发表于赖勇浩(恋花蝶)的博客(http://blog.csdn.net/lanphaday),如蒙转载,敬请保留全文完整,切勿去除本声明和作者信息.        ...

  3. python time模块详解

    python time模块详解 转自:http://blog.csdn.net/kiki113/article/details/4033017 python 的内嵌time模板翻译及说明  一.简介 ...

  4. Python中dict详解

    from:http://www.cnblogs.com/yangyongzhi/archive/2012/09/17/2688326.html Python中dict详解 python3.0以上,pr ...

  5. Python开发技术详解(视频+源码+文档)

    Python, 是一种面向对象.直译式计算机程序设计语言.Python语法简捷而清晰,具有丰富和强大的类库.它常被昵称为胶水语言,它能够很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松地联结 ...

  6. python/ORM操作详解

    一.python/ORM操作详解 ===================增==================== models.UserInfo.objects.create(title='alex ...

  7. 【python进阶】详解元类及其应用2

    前言 在上一篇文章[python进阶]详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~ 5.使⽤type创建带有 ...

  8. Python开发技术详解PDF

    Python开发技术详解(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1F5J9mFfHKgwhkC5KuPd0Pw 提取码:xxy3 复制这段内容后打开百度网盘手 ...

  9. python之数据类型详解

    python之数据类型详解 二.列表list  (可以存储多个值)(列表内数字不需要加引号) sort s1=[','!'] # s1.sort() # print(s1) -->['!', ' ...

随机推荐

  1. 怎样使用yum-cron为CentOS7自动更新重要的安全补丁

    怎样使用yum-cron为CentOS自动更新重要的安全补丁 2017年4月19日 | 分类: [技术] 参考:https://linux.die.net/man/8/yum-cron参考:http: ...

  2. Java基础语法,常用知识复习

    1.开发环境搭建 1.1JDK.JRE.JVM.的关系 JDK = JRE + 开发工具集(例如javac编译工具集等) JRE = JVM + Java SE 标准类库 2.基本语法 2.1.jav ...

  3. MongoDB(8)- 文档删除操作

    删除方法 db.collection.deleteOne() 删除单条文档 db.collection.deleteMany() 删除多条文档 db.collection.remove() 删除单条或 ...

  4. Jmeter+Ant+Jenkins接口自动化框架

    最近应公司要求,搭建一套接口自动化环境.看到通知邮件,没有多想就确定了Jmeter路线.可能有些人会 说,为啥不用python,相对而言高大上一些.因为公司内部现在项目有用到Jmeter,正好可以结合 ...

  5. Hadoop 基石HDFS 一文了解文件存储系统

    @ 目录 前言:浅谈Hadoop Hadoop的发展历程 1.1 Hadoop产生背景 1.引入HDFS设计 1.1 HDFS主要特性 2.HDFS体系结构 HDFS工作流程机制 1.各个节点是如何互 ...

  6. IDEA debug ConcurrentLinkedQueue时抽风

    1. 介绍 如标题所见,我在使用IDEA debug ConcurrentLinkedQueue的Offer方法时,发生了下面的情况. 代码如下: ConcurrentLinkedQueue<s ...

  7. 一文读懂一条 SQL 查询语句是如何执行的

    2001 年 MySQL 发布 3.23 版本,自此便开始获得广泛应用,随着不断地升级迭代,至今 MySQL 已经走过了 20 个年头. 为了充分发挥 MySQL 的性能并顺利地使用,就必须正确理解其 ...

  8. Nginx 配置实例-配置虚拟主机

    Nginx 配置实例-配置虚拟主机 配置基于域名的虚拟主机 1. 配置域名为 aaa.domain.com 的虚拟主机 1.1 nginx 中虚拟主机的配置 1.2 相关目录及文件的创建 1.3 验证 ...

  9. Docker学习(9) Docker守护进程的配置和操作

  10. Spring的controller接受Date类型数据,接受枚举类型数据

    1. Controller接收Date类型的数据 核心使用@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 来将传递过来的时间字符串 ...