【Python系列专栏】第三十九篇 Python中常用内建模块(struct)

struct

为何需要struct

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

假设我们要把一个32位无符号整数转换为字节(4个bytes)。在Python中,得这么写:

>>> n = 10240099
>>> b1 = (n & 0xff000000) >> 24
>>> b2 = (n & 0xff0000) >> 16
>>> b3 = (n & 0xff00) >> 8
>>> b4 = n & 0xff
>>> bs = bytes([b1, b2, b3, b4])
>>> bs
b'\x00\x9c@c'

稍微解析一下,十进制数 10240099 转换为十六进制是 0x009c4063,这里使用与运算和右移来拆分出这个32位无符号整数的每一个byte(注意得到的不是字节数组而是一个整数),其中 b1=0, b2=156, b3=64, b4=99,将这四个整数放入一个列表中传入 bytes() 函数,就能得到字节数组了。注意 bs 中是有4个字节的,len(bs) 的值为4。由于对应整数64的十六进制数 0x40 属于ASCII码范围,所以用字符 @ 表示,而对应整数99的十六进制数 0x63 则对应字符 c

可以看到,这样要逐个byte来拆分,再进行转换实在是非常麻烦。如果要把浮点数转换为字节就无能为力了。幸好,Python提供了一个 struct 模块来解决 bytes 和其他二进制数据类型的转换问题。


struct的用法

struct 模块的 pack 函数把任意数据类型变成 bytes

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

pack() 的第一个参数是处理指令,这里 '>I' 的意思分为两部分:

  • 字节顺序:> 表示的是使用大端序作为字节顺序。
  • 数据类型:I 表示的是要转换一个32位无符号整数。可以有多个数据类型从而转换出多个数。

第二个参数要注意和处理指令一致。

unpack() 函数与 pack() 相反,它是把 bytes 转换为相应的数据类型:

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

这里的 >IH 表示这个字节数组用的是大端序,并且要依次转换出一个32位无符号整数和16位无符号整数。

struct 模块定义的数据类型可以参考Python官方文档。关于大端序(big-endian,BE)和小端序(little-endian,LE)的区别可以看看这篇博文,讲解得很清晰。


使用struct分析bmp文件

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

首先找到一个bmp文件,没有的话用Windows自带的【画图】画一个即可。读入它的前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位图;
  • 一个32位无符号整数:表示位图大小;
  • 一个32位无符号整数:保留位,始终为0;
  • 一个32位无符号整数:实际图像的偏移量;
  • 一个32位无符号整数:Header的字节数;
  • 一个32位无符号整数:图像宽度(单位为像素);
  • 一个32位无符号整数:图像高度(单位为像素);
  • 一个16位无符号整数:始终为1;
  • 一个16位无符号整数:颜色数。

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

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

这里的 c 表示对应的数据类型是一个字符。结果显示,b'B'b'M' 说明是一张Windows位图,位图大小为640x360,颜色数为24。


小结

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


练习

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

代码:

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

import sys
import struct

def readBmpFile(file):

    f = open(file, 'rb')
    bs = f.read()
    f.close()
    return bs[0:30]

def checkBmp(info):

    ts = struct.unpack('<ccIIIIIIHH', info)

    if ts[0] == b'B' and ts[1] == b'M':
        print('图片大小:%d * %d' % (ts[6], ts[7]))
        print('颜色数:%d' % ts[9])
    else:
        print('非位图文件')

if __name__=='__main__':
    if len(sys.argv) == 2:
        info = readBmpFile(sys.argv[1])
        checkBmp(info)
    else:
        info = input('Please input the file name: ')
        checkBmp(info)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mrrunsen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值