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. python基础之面向对象(二)(封装、继承、多态)

    一.封装 (1)封装是面向对象的一大特点 (2)面向对象编程的第一步--将属性和方法封装到一个抽象的类当中 (3)外界使用类创建对象,然后让对象调用方法 (4)对象方法的细节都被封装在类的内部 1.案 ...

  2. python基础之面向对象(一)(概念、实例、魔法方法)

    一.面向对象概念理解 1.面向对象和面向过程 面向过程:核心过程二字,过程即解决问题的步骤,就是先干什么后干什么 基于该思想写程序就好比在这是一条流水线,是一种机械式的思维方式 优点:复杂的过程流程化 ...

  3. 7.2-5 usermod

    7.2 usermod:修改用户信息 usermod 命令用于修改系统已经存在的用户的账号信息.   -c comment         修改用户password文件中用户的说明栏,同useradd ...

  4. Windows上能看朋友圈的微信来了 | 附下载地址

    昨天的时候,电脑端的微信提示更新就顺手更新了一下,更新完成后习惯性的点了下设置,纳尼,居然被灰到了测试版本? 带着好奇,赶快看了下更新了什么内容: 支持浏览朋友圈 "搜一搜"支持搜 ...

  5. 第12讲 | TCP协议(下):西行必定多妖孽,恒心智慧消磨难

    第12讲 | TCP协议(下):西行必定多妖孽,恒心智慧消磨难 如何做个靠谱的人? 有问有答,任务发送和接收有记录,完成有反馈. 如何实现一个靠谱的协议? TCP 协议使用的也是同样的模式.为了保证顺 ...

  6. 深入剖析 MySQL 自增锁

    之前的文章把 InnoDB 中的所有的锁都介绍了一下,包括意向锁.记录锁...自增锁巴拉巴拉的.但是后面我自己回过头去看的时候发现,对自增锁的介绍居然才短短的一段. 其实自增锁(AUTO-INC Lo ...

  7. 【Azure 环境】Azure Key Vault (密钥保管库)中所保管的Keys, Secrets,Certificates是否可以实现数据粒度的权限控制呢?

    问题描述 Key Vault (密钥保管库) 能不能针对用户授权实现指定用户只能访问某个或某些特定的key? 如当前有两个用户(User1, User2),在Key Vault中有10个Key,Use ...

  8. Netty 框架学习 —— Netty 组件与设计

    Channel.EventLoop 和 ChannelFuture 这一节将对 Channel.EventLoop 和 ChannelFuture 类进行讨论,它们组合在一起,可以被认为是 Netty ...

  9. 三:.net core(.NET 6)给swagger添加文档注释详细步骤

    提供swagger文档注释. 0.先给api加上标题注释和返回值注释: 然后,启动见证奇迹: What the hell?没得注释?查看当前自动生成的swagger代码如下: 首先,我们需要对需要注释 ...

  10. 面试侃集合 | SynchronousQueue公平模式篇

    面试官:呦,小伙子来的挺早啊! Hydra:那是,不能让您等太久了啊(别废话了快开始吧,还赶着去下一场呢). 面试官:前面两轮表现还不错,那我们今天继续说说队列中的SynchronousQueue吧. ...