前言
今天这篇博文有点意思,它是从一个例子出发,从而体现出在编程中的种种细节和一些知识点的运用。和从前一样,我是人,离成神还有几十万里,所以无可避免的出现不严谨的地方甚至错误,请酌情阅读。
0x00
首先,题目是:读取一个位图文件(xxx.bmp),然后读取前30个字节,从这前三十个字节中提取一些信息。
这里有一些知识要先知道:一个位图的前30位有什么?
BMP格式采用小端方式存储数据,文件头的结构按顺序如下:
- 前两个字节:\'BM\'表示Windows位图,\'BA\'表示OS/2位图;
- 一个4字节整数:表示位图大小;
- 一个4字节整数:保留位,始终为0;
- 一个4字节整数:实际图像的偏移量;
- 一个4字节整数:Header的字节数;
- 一个4字节整数:图像宽度;
- 一个4字节整数:图像高度;
- 一个2字节整数:始终为1;
- 一个2字节整数:颜色数。
你先要知道,读取一个位图的前30位,那么这前30位是什么编码呢?例如,某个位图的前30位:
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\'
很容易看出,它们的单位是bytes
,也就是说,我们无法直接看懂它们,因此,我们得把它转换成我们能看懂的东西。
**python有趣的地方就在这里了,Python提供了一个struct
模块来解决bytes
和数据类型的转换。 **
例如,我想把一个整数10241024
转换为以bytes
为单位的字节怎么办?(如果你不懂字节编码,看这里)很简单:
>>> struct.pack(\'>I\', 10241024)
b\'\\x00\\x9cD\\x00\'
稍微解释一下:
>
表示字节顺序是big-endian,也就是网络序,I
表示4字节无符号整数。
那么我想把以bytes
为单位的字节转换回整型怎么办?,也很简单:
>>> struct.unpack(\'>I\', b\'\\x00\\x9cD\\x00\')
(10241024,)
要注意的地方就是unpack
函数的第一个参数:\'>I\'
,解释和pack
函数的一样。
0x01
到了这里,基本可以看例子了。说明:
- 我的执行环境是:python 3.6;
data.bmp
是我用Windows自带画图随手画的一个位图图片;- 我把这个程序保存为:
struct1.py
; data.bmp
和struct1.py
同在一个Windows目录下,完整路径:G:\\newpython\\data.bmp
和G:\\newpython\\struct1.py
例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import struct
from collections import namedtuple
def bmg_info(weitu):
with open(weitu, \'rb\') as f:
data = f.read(30)
info = BMGinfo(*struct.unpack(\'<ccIIIIIIHH\', data))
if info.ch1 == b\'B\' and (info.ch2 == b\'M\' or info.ch2 == b\'A\'):
return {
\'width\': info.width,
\'height\': info.height,
\'color\': info.colornum
}
return None
if __name__ == "__main__":
BMGinfo = namedtuple(\'BMGinfo\', (\'ch1\',\'ch2\',\'size\',\'reserved\',\'offset\',\'headerbits\',\'width\',\'height\',\'num\',\'colornum\'))
weitu = sys.argv[1]
print(bmg_info(weitu))
打开cmd
,进入目录G:\\newpython
,执行命令:python struct1.py "G:\\newpython\\data.bmp"
即可看见惊喜。
这篇博客说到这里,终于到了重要的地方。其实在实践这段代码时,我遇见了几个问题(错误):
- 遇到错误(AttributeError: module \'struct\' has no attribute \'unpack\'),原来是我自己的py文件不要和系统的模块名重名,解决办法,把文件名改掉(我这里把
struct.py
改为struct1.py
); - 灵活运用namedtuple。了解更多请看这里
- 灵活运用可变参数技能(*struct.unpack(\'<ccIIIIIIHH\', data)),关于可变参数,看这里
0x10
感谢阅读。