{{ cat }}'s docs

3.2.0 如何处理二进制文件


0. 目标

学会如何处理二进制文件,以wav文件举例。

wav文件格式

补充:自己下载了一个wav的文件,但是因为下载的文件和老师实例中的不同,所以第4步


1. 获取二进制信息

>>> f = open("demo.wav", "rb")

>>> info = f.read(44)

>>> info
'RIFFv\xea\xda\x01WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00D\xac\x00\x00\x10\xb1\x02\x00\x04\x00\x10\x00LIST\x1a\x00\x00\x00'

"b"mode使用二进制模式打开文件,默认是text模式(t)
根据wav的文件格式,我们知道前44个字节是文件信息


2. 使用struct解析二进制信息

>>> import struct
# 解包info数据,通过切片获得声道数值
# 因为采样频率是2个字节,使用h->short类型(c中的数据类型)
>>> struct.unpack("h", info[22:24])
(2,)

# 获取采样频率数值
# 因为采样频率数值是4个字节,使用i->int类型(c中的数据类型)
>>> struct.unpack("i", info[24:28])
(44100,)

# 获取编码宽度数值
>>> struct.unpack("h",info[34:36])
(16,)

struct 官方文档


3. 将文件的data部分储存在数组中

之所以不使用struct来获取data部分信息,是对于data部分数据,进行处理的话,如果是字符串,不如数值来的方便
所以,我们将data部分数据储存到数组当中

>>> import array

## 获取文件的长度,用来推算数组长度
>>> f.seek(0, 2)
# 0代表offset为0,2代表文件末尾,也就是说我们将指针调整到文件末尾

# 使用f.tell()来获取目前指针位置
>>> f.tell()
31124094

# 计算数组长度(这里计算中的2是根据声道数来确定的)
>>> array_len = (f.tell() - 44) / 2

# 创建一个初始化长度为array_len的数组
>>> buf = array.array('h', (0 for _ in xrange(array_len)))
# 将data部分数据读入到buf这个数组中去
>>> f.seek(44)
>>> f.readinto(buf)
31124050

4. 处理data部分数据(示例:调整采样声音长度来改变声音大小)

# 给每个采样做强度缩减操作
>>> for i in xrange(array_len):
...     buf[i] /= 8
...     

>>> fnew = open('demo2.wav', 'wb')

>>> fnew.write(info)

>>> buf.tofile(fnew)

>>> fnew.close()

此时可以打开demo2.wav文件,发现声音变小,因为我们给每个采样做了长度缩减操作