python struct 用法_Python标准库笔记struct模块的使用

最近在学习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'),

('

('>', '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)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值