在解析自定义的二进制的文件格式,或者已知的一些文件格式的时候,比如png图片,tcp包,就需要struct库;
解析二进制文件长需要的函数有从buffer中读取char(一个字节),short(两个字节),int(4字节),string(n个char),这些struct都可以很好的胜任。官方说明
struct生成或者解析二进制的使用方法,很像格式化输出,即使用特定的字符来输出自己想要的格式;
函数说明
except:struct.error
struct.pack(fmt,v1,v2,...): 返回以fmt格式指定的v1,v2,…组成的string; 要求v1,v2,...能够严格匹配fmt
struct.pack_into(fmt,buffer,offset,v1,v2,...): 从buffer的offset处,组成以fmt格式指定组成的v1,v2,...
struct.unpack(fmt,string): 使用fmt解析string,返回结果为tuple,即使只包含一个值。要求len(string)与calcsize(fmt)相同
struct.unpack_from(fmt,buffer[,offset=0]): 从buffer的offset处开始以fmt指定的格式解析,返回结果为tuple,要求len(buffer[offset:])>=calcsize(fmt)
struct.calcsize(fmt): 计算给定格式的字节大小
fmt可以指定字节序,字符类型等
大小端
做过相关网络数据包开发的都接触过本地序和网络序;
对于网络序,使用的大端序(big endian)。
本地序跟CPU相关
对于Intel X86和AMD64(X86-64)都是小端序(little-endian)
Motorola 68000 和 PowerPC G5是大端序
对于ARM和Intel Itanium 是双端序
可以使用sys.byteorder来获取当前系统的类型。
那么什么是大端序,小端序?
大端序:数据的高位字节存放在地址的低端,低位字节存放在地址的高端
小端序:数据的高位自己存放在地址的高端,低位字节存放在地址的低端
数据的高位和低位:举例,int a = 0x12345678, 坐标12是高位字节,右边78是低位字节,从左到右,从高到低;
地址的高端和低端(假设是64位机器,一个地址是64位):
0x00000001
0x00000002
0x00000003
0x00000004
从上到下,由低到高,地址值小的为低端,地址值大的为高端。
假设从地址0x00000001开始存储数0x12345678,则
大端序存放方式(按原来顺序存放):
0x00000001 –12
0x00000002 –34
0x00000003 –56
0x00000004 –78
小端序存放方式(按颠倒顺序存放):
0x00000001 –78
0x00000002 –56
0x00000003 –34
0x00000004 –12
struct可以指定使用大端序还是小端序来解析或者生成数据。
| Character | Byte order | Size | Alignment |
|---|---|---|---|
| @ | native | native | native |
| = | native | standard | none |
| < | little-endian | standard | none |
| > | big-endian | standard | none |
| ! | network(=big-endian) | standard | none |
如果不指定,@是默认值。
Native size和alignment由C语言的sizeof表达式决定, 即结构体的字节对齐规则。C语言的字节对齐问题
数据格式
| Format | C Type | Python type | Standard size | Notes |
|---|---|---|---|---|
| x | pad byte | no value | ||
| c | char | string of length 1 | 1 | |
| b | signed char | integer | 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) |
| f | float | float | 4 | (4) |
| d | double | float | 8 | (4) |
| s | char[] | string | ||
| p | char[] | string | ||
| P | void * | integer | (5),(3) |
Notes:
?是C99中的_Bool, 如果不可用, 则生成char, 一个字节;q和Q只用当C编译器支持long long;当pack一个非integer类型的时候,如果此非integer有
__index__()函数,则pack__init__()的返回值;若没有__index__()或者调用__index__()报异常,则尝试__int__()函数,若没有__int__()则报DeprecationWarning;对于浮点类型,
f使用IEEE 754 binary32,d使用binary64标准P只在native byte ordering使用4h等价于hhhh'10s'代表 10-byte string,'10c'代表 10 charactersp即Pascal string, 第一个字节代表长度,随后是字符串
举例
示例在big-endian机器,以native byte order,size, alignment
1
2
3
4
5
6
7
8 > >>> from struct import *
> >>> pack('hhl', 1, 2, 3)
> '\x00\x01\x00\x02\x00\x00\x00\x03'
> >>> unpack('hhl', '\x00\x01\x00\x02\x00\x00\x00\x03')
> (1, 2, 3)
> >>> calcsize('hhl')
> 8
>
示例代码
1 | import struct |