Python学习(八)

学习网址:廖雪峰的Python教程

struct模块

struct模块是Python来解决bytes和其他二进制数据类型的转换.

pack函数

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

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

pack的第一个参数是处理指令,”>I“的意思是:

>表示字节顺序是big-endian,就是网络序, I 表示4字节无符号整数.

后面的参数个数要和处理指令一致.

unpack函数

unpack函数可以把bytes变成相应的数据类型:

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

“>IH”表示,后面bytes依次变为I: 4字节无符号整数和H: 2字节无符号整数.

struct参考文档

使用struct分析windows位图文件(bmp),bmp是一种简单的文件格式.

首先,找到一个bmp文件,读入前30个字节来分析:

1
>>> 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位图;

一个4字节整数:表示位图大小;

一个4字节整数:保留位,始终为0;

一个4字节整数:实际图像的偏移量;

一个4字节整数:Header的字节数;

一个4字节整数:图像宽度;

一个4字节整数:图像高度;

一个2字节整数:始终为1;

一个2字节整数:颜色数。

组合起来用struct读取:

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

b’B’,b’M’表示是windows位图,位图大小是:640*360,颜色数是24

练习:

检查一个文件是否是位图文件 ,如果是就打印出图片大小和颜色数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import struct
def isbmp(files):
with open(files, 'rb') as fp:
bitOfBmp = fp.read(30)
if(len(bitOfBmp) < 30):
print('The file is not a bitmap.')
else:
fileInfo = struct.unpack('<ccIIIIIIHH', bitOfBmp)
if(fileInfo[0] == b'B' and fileInfo[1] == b'M'):
print('The size of BitMap is %s * %s, #color is %s' % (fileInfo[6], fileInfo[7], fileInfo[9]))
else:
print('The file is not a bitmap for windows.')
if (name == 'main'):
isbmp(r'C:/Users/snow/Desktop/img/6.bmp')

hashlib

摘要算法:

hashlib模块为Python提供了常见的摘要算法,如MD5,SHA1.

摘要算法又称哈希算法,散列算法,通过一个函数,把任意长度的数据转换为一个长度固定的数据串,(用16进制的字符串表示).

通过摘要函数f(),对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过.

摘要函数是一个单向函数,计算f(data)简单,但通过digest反过来推算data很困难,原始数据的一个bit的修改,都会导致计算出的结果不同.

MD5
1
2
3
4
>>> import hashlib
>>> md5 = hashlib.md5()
>>> md5.update('how to introduce yourself?'.encode('utf-8'))
>>> print(md5.hexdigest())

结果:

1
84cbf48524a29ac5ca1eed0b68468a0a

MD5是最常见的摘要算法,速度很快,生成结果是固定的128bit字节,通常用一个32位的16进制字符串表示.

SHA1

使用方法和MD5类似:

1
2
3
4
>>> import hashlib
>>> sh = hashlib.sha1()
>>> sh.update('how to introduce yourself?'.encode('utf-8'))
>>> print(sh.hexdigest())

结果:

1
68f18af3fab0438265d5d6675bb74e5a41a2e585

SHA1的结果是160bit字节,用40位的16进制字符串表示.

比SHA1更安全的算法是,SHA256和SHA512,但是,越安全的算法会越慢,摘要长度也更长.

摘要算法的应用

在用户登录网站时,首先计算用户输入的明文口令的MD5,然后和数据库存储的MD5进行比较,如果一直,说明口令正确,不一致,口令错误.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import hashlib
def login(user,password):
db = {
'michael': 'e10adc3949ba59abbe56e057f20f883e',
'bob': '878ef96e86145580c38c87f0410ad153',
'alice': '99b1c2188db85afee403b1536010c2c9'
}
md5 = hashlib.md5()
md5.update(password.encode('utf-8'))
if user == 'michael':
if md5.hexdigest() == db['michael']:
print('michael password is true')
else:
print('michael password is false')
elif user=='bob':
if md5.hexdigest() == db['bob']:
print('bob password is true')
else:
print('bob password is false')
elif user == 'alice':
if md5.hexdigest() == db['alice']:
print('alice password is true')
else:
print('alice password is false')
if __name__ == '__main__':
login('michael','123456')
#login('bob','abc99')
#login('michael','alice2008')

有了MD5口令也不一定安全,对于常用口令的MD5值很容易算出来,在原始口令基础上加上一个复杂字符串,再进行计算MD5的值,可以提高口令的安全性.这种方法俗称”加盐”.

1
2
def salt_MD5(password):
return get_md5(password + 'the-Salt')

salt处理后的MD5值,只要salt不被外人知道,很难通过MD5反推明文.

摘要算法不是加密算法,不能用于加密(无法通过摘要反推明文),只能用于防篡改,有单向计算的特性.

itertools

Python内建模块itertools提供了非常有用的用于操作迭代对象的函数.

count()

count()是itertools中提供的无限迭代器,一直打印自然数列,按 ctrl+c 停止.

1
2
3
4
5
6
7
8
>>> import itertools
>>> nums = itertools.count()
>>> for num in nums:
... print(num)
1
2
3
...
cycle()

cycle()方法是会进行重复循环输出.ctrl + c 停止输出.

1
2
3
4
5
6
7
8
9
10
11
12
>>> import itertools
>>> cyc = itertools.cycle('SNOW')
>>> for c in cyc:
... print(c)
S
N
O
W
S
N
O
W
repeat()

repeat()函数,负责把一个元素无限重复,第二个参数可以规定重复的次数:

1
2
3
4
5
6
7
8
9
>>> import itertools
>>> re = itertools.repeat('s',4)
>>> for r in re:
... print(r)
...
s
s
s
s
takewhile()

截取一个有序的序列:

1
2
3
4
>>> num = itertools.count(3)
>>> n = itertools.takewhile(lambda x: x <= 10,num)
>>> list(n)
[3, 4, 5, 6, 7, 8, 9, 10]

count()中的参数,是起始位置,talkwhile()截取了从起始位置开始到小于等于10之间的数字序列.

chain()

chain()可以把一组迭代对象串联起来,

1
2
3
4
5
6
7
8
9
10
>>> import itertools
>>> for w in itertools.chain('ABC','XYZ'):
... print(w)
...
A
B
C
X
Y
Z
groupby()

把迭代器中相邻的重复元素挑出来放在一起.

1
2
3
4
5
6
7
>>> for w,word in itertools.groupby('WWWWWOOOOOOOORRRDDDDDD'):
... print(w,list(word))
...
W ['W', 'W', 'W', 'W', 'W']
O ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']
R ['R', 'R', 'R']
D ['D', 'D', 'D', 'D', 'D', 'D']
1
2
3
4
5
6
7
>>> for k,word in itertools.groupby('AaaaaBbBbbcccCCDddd',lambda c: c.upper()):
... print(k,list(word))
...
A ['A', 'a', 'a', 'a', 'a']
B ['B', 'b', 'B', 'b', 'b']
C ['c', 'c', 'c', 'C', 'C']
D ['D', 'd', 'd', 'd']

挑选规则是通过函数完成的,只要作用于函数的两个元素返回的值相等,就被认为是在一组的,函数返回值作为组的key,忽略大小写分组,就可以让”A”和”a”返回相同的值key.

itertools模块提供的函数,全部都是处理迭代的,返回的是Iterator,只有用for进行迭代时才能正确的计算.


真诚地希望能帮到你!