python结构体_python struct 结构体

Python 的 struct 模块用于处理二进制数据,模仿 C 语言中的结构体。关键函数包括 pack()、unpack() 和 calcsize()。pack() 将数据封装成字节流,unpack() 解析字节流,calcsize() 计算格式所占内存。struct 支持多种格式,如 b 对应 signed char,H 对应 unsigned short 等。字节顺序可通过 '@', '=', '<', '>' 和 '!' 控制。示例展示了如何使用 struct 进行数据转换和在网络中传输结构体数据。" 105982993,7372355,Unity初学者教程:创建简单射击游戏,"['Unity3d', '游戏开发', 'C#']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import struct

有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体.

struct模块中最重要的三个函数是pack(), unpack(), calcsize()

pack(fmt, v1, v2, ...) 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)

unpack(fmt, string) 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple

calcsize(fmt) 计算给定的格式(fmt)占用多少字节的内存

struct中支持的格式如下表:

Format

C Type

Python

字节数x

pad byte

no value

1

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

unsigned int

integer or long

4

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

1

p

char[]

string

1

P

void *

long

注1.q和Q只在机器支持64位操作时有意思

注2.每个格式前可以有一个数字,表示个数

注3.s格式表示一定长度的字符串,4s表示长度为4的字符串,但是p表示的是pascal字符串

注4.P用来转换一个指针,其长度和机器字长相关

注5.最后一个可以用来表示指针类型的,占4个字节

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

Character

Byte order

Size and alignment@

native

native 凑够4个字节

=

native

standard 按原字节数

<

little-endian

standard 按原字节数

>

big-endian

standard 按原字节数

!

network (= big-endian)

standard 按原字节数

使用方法是放在fmt的第一个位置,就像'@5s6sif'

示例一:

比如有一个结构体

struct Header

{

unsigned short id;

char[4] tag;

unsigned int version;

unsigned int count;

}

通过socket.recv接收到了一个上面的结构体数据,存在字符串s中,现在需要把它解析出来,可以使用unpack()函数.

import struct

id, tag, version, count = struct.unpack("!H4s2I", s)

上面的格式字符串中,!表示我们要使用网络字节顺序解析,因为我们的数据是从网络中接收到的,在网络上传送的时候它是网络字节顺序的.后面的H表示 一个unsigned short的id,4s表示4字节长的字符串,2I表示有两个unsigned int类型的数据.

就通过一个unpack,现在id, tag, version, count里已经保存好我们的信息了.

同样,也可以很方便的把本地数据再pack成struct格式.

ss = struct.pack("!H4s2I", id, tag, version, count);

pack函数就把id, tag, version, count按照指定的格式转换成了结构体Header,ss现在是一个字符串(实际上是类似于c结构体的字节流),可以通过 socket.send(ss)把这个字符串发送出去.

示例二:

import struct

a=12 #将a变为二进制

bytes=struct.pack('i',a)

此时bytes就是一个string字符串,字符串按字节同a的二进制存储内容相同。

再进行反操作

现有二进制数据bytes,(其实就是字符串),将它反过来转换成python的数据类型:

a,=struct.unpack('i',bytes)

注意,unpack返回的是tuple

所以如果只有一个变量的话:

bytes=struct.pack('i',a)

那么,解码的时候需要这样

a,=struct.unpack('i',bytes) 或者 (a,)=struct.unpack('i',bytes)

如果直接用a=struct.unpack('i',bytes),那么 a=(12,) ,是一个tuple而不是原来的浮点数了。

如果是由多个数据构成的,可以这样:

a='hello'

b='world!'

c=2

d=45.123

bytes=struct.pack('5s6sif',a,b,c,d)

此时的bytes就是二进制形式的数据了,可以直接写入文件比如 binfile.write(bytes)

然后,当我们需要时可以再读出来,bytes=binfile.read()

再通过struct.unpack()解码成python变量

a,b,c,d=struct.unpack('5s6sif',bytes)

'5s6sif'这个叫做fmt,就是格式化字符串,由数字加字符构成,5s表示占5个字符的字符串,2i,表示2个整数等等,下面是可用的字符及类型,ctype表示可以与python中的类型一一对应。

注意:二进制文件处理时会碰到的问题

我们使用处理二进制文件时,需要用如下方法

binfile=open(filepath,'rb') 读二进制文件

binfile=open(filepath,'wb') 写二进制文件

那么和binfile=open(filepath,'r')的结果到底有何不同呢?

不同之处有两个地方:

第一,使用'r'的时候如果碰到'0x1A',就会视为文件结束,这就是EOF。使用'rb'则不存在这个问题。即,如果你用二进制写入再用文本读出的话,如果其中存在'0X1A',就只会读出文件的一部分。使用'rb'的时候会一直读到文件末尾。

第二,对于字符串x='abc\ndef',我们可用len(x)得到它的长度为7,\n我们称之为换行符,实际上是'0X0A'。当我们用'w'即文本方式写的时候,在windows平台上会自动将'0X0A'变成两个字符'0X0D','0X0A',即文件长度实际上变成8.。当用'r'文本方式读取时,又自动的转换成原来的换行符。如果换成'wb'二进制方式来写的话,则会保持一个字符不变,读取时也是原样读取。所以如果用文本方式写入,用二进制方式读取的话,就要考虑这多出的一个字节了。'0X0D'又称回车符。linux下不会变。因为linux只使用'0X0A'来表示换行。

附加:

import struct

# native byteorder

buffer = struct.pack("ihb", 1, 2, 3)

print repr(buffer)

print struct.unpack("ihb", buffer)

# data from a sequence, network byteorder

data = [1, 2, 3]

buffer = struct.pack("!ihb", *data)

print repr(buffer)

print struct.unpack("!ihb", buffer)

Output:

'\x01\x00\x00\x00\x02\x00\x03'

(1, 2, 3)

'\x00\x00\x00\x01\x00\x02\x03'

(1, 2, 3)

首先将参数1,2,3打包,打包前1,2,3明显属于python数据类型中的integer,pack后就变成了C结构的二进制串,转成 python的string类型来显示就是  '\x01\x00\x00\x00\x02\x00\x03'。由于本机是小端('little- endian',关于大端和小端的区别请参照这里,故而高位放在低地址段。i 代表C struct中的int类型,故而本机占4位,1则表示为01000000; h 代表C struct中的short类型,占2位,故表示为0200;同理b 代表C struct中的signed char类型,占1位,故而表示为03。

其他结构的转换也类似,有些特别的可以参考Manual。

在Format string 的首位,有一个可选字符来决定大端和小端,列表如下:

Character

Byte order

Size

Alignment@

native

native

native

=

native

standard

none

<

little-endian

standard

none

>

big-endian

standard

none

!

network (= big-endian)

standard

none

还有一个标准的选项,被描述为:如果使用标准的,则任何类型都无内存对齐。

如果没有附加,默认为@,即使用本机的字符顺序(大端or小端),对于C结构的大小和内存中的对齐方式也是与本机相一致的(native),比如有的机器integer为2位而有的机器则为四位;有的机器内存对其位四位对齐,有的则是n位对齐(n未知,我也不知道多少)。

比如刚才的小程序的后半部分,使用的format string中首位为!,即为大端模式标准对齐方式,故而输出的为'\x00\x00\x00\x01 \x00\x02 \x03',其中高位自己就被放在内存的高地址位了。

=======================================================================

Python struct模块

用处

按照指定格式将Python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时先将int转化为字节流,然后再发送;

按照指定格式将字节流转换为Python指定的数据类型;

处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件;

处理c语言中的结构体;

struct模块中的函数

函数

return

explainpack(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 sizex

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

注- -!

_Bool在C99中定义,如果没有这个类型,则将这个类型视为char,一个字节;

q和Q只适用于64位机器;

每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int;

P用来转换一个指针,其长度和计算机相关;

f和d的长度和计算机相关;

code,使用示例

#!/usr/bin/python

# -*- coding:utf-8 -*-

'''测试struct模块'''

from struct import *

import array

def fun_calcsize():

print 'ci:',calcsize('ci')#计算格式占内存大小

print '@ci:',calcsize('@ci')

print '=ci:',calcsize('=ci')

print '>ci:',calcsize('>ci')

print '

print 'ic:',calcsize('ic')#计算格式占内存大小

print '@ic:',calcsize('@ic')

print '=ic:',calcsize('=ic')

print '>ic:',calcsize('>ic')

print '

def fun_pack(Format,msg = [0x11223344,0x55667788]):

result = pack(Format,*msg)

print 'pack'.ljust(10),str(type(result)).ljust(20),

for i in result:

print hex(ord(i)), # ord把ASCII码表中的字符转换成对应的整形,hex将数值转化为十六进制

print

result = unpack(Format,result)

print 'unpack'.ljust(10),str(type(result)).ljust(20),

for i in result:

print hex(i),

print

def fun_pack_into(Format,msg = [0x11223344,0x55667788]):

r = array.array('c',' '*8)#大小为8的可变缓冲区,writable buffer

result = pack_into(Format,r,0,*msg)

print 'pack_into'.ljust(10),str(type(result)).ljust(20),

for i in r.tostring():

print hex(ord(i)),

print

result = unpack_from(Format,r,0)

print 'pack_from'.ljust(10),str(type(result)).ljust(20),

for i in result:

print hex(i),

print

def IsBig_Endian():

'''判断本机为大/小端'''

a = 0x12345678

result = pack('i',a)#此时result就是一个string字符串,字符串按字节同a的二进制存储内容相同。

if hex(ord(result[0])) == '0x78':

print '本机为小端'

else:

print '本机为大端'

def test():

a = '1234'

for i in a:

print '字符%s的二进制:'%i,hex(ord(i))#字符对应ascii码表中对应整数的十六进制

'''

不用unpack()返回的数据也是可以使用pack()函数的,只要解包的字符串符合解包格式即可,

pack()会按照解包格式将字符串在内存中的二进制重新解释(说的感觉不太好...,见下例)

'''

print '大端:',hex(unpack('>i',a)[0])#因为pack返回的是元组,即使只有一个元素也是元组的形式

print '小端:',hex(unpack('

if __name__ == "__main__":

print '判断本机是否为大小端?',

IsBig_Endian()

fun_calcsize()

print '大端:'

Format = ">ii"

fun_pack(Format)

fun_pack_into(Format)

print '小端:'

Format = "

fun_pack(Format)

fun_pack_into(Format)

print 'test'

test()

'''

result:

判断本机是否为大小端? 本机为小端

ci: 8

@ci: 8

=ci: 5

>ci: 5

ic: 5

@ic: 5

=ic: 5

>ic: 5

大端:

pack 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88

unpack 0x11223344 0x55667788

pack_into 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88

pack_from 0x11223344 0x55667788

小端:

pack 0x44 0x33 0x22 0x11 0x88 0x77 0x66 0x55

unpack 0x11223344 0x55667788

pack_into 0x44 0x33 0x22 0x11 0x88 0x77 0x66 0x55

pack_from 0x11223344 0x55667788

test

字符1的二进制: 0x31

字符2的二进制: 0x32

字符3的二进制: 0x33

字符4的二进制: 0x34

大端:0x31323334

小端:0x34333231

'''

英文单词

英文

中文compact

紧凑的,简洁的

layout

布局

pad bytes

填充字节

alignment

对齐方式

maintain

维持,保持

proper

适当的

correspond

一致,相应的

platform-independent

平台依赖,即机器,操作系统不同,对齐方式,大小端等会有差异

omit

忽略

implicit

暗含的,隐式的

native

本台电脑的

presumably

假设

even if

即使

specify

指定

mechanism

原理,机制,手法

represented

表现,表示

assumed

假定的,默认的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值