如何处理二进制文件?

需求:
wav是一种音频文件的格式,音频文件为二进制文件,wav文件由头部信息和音频采样数据组成,前44个字节为头部信息,包括声道数,采样频率,PCM位宽等等,后面是音频采样数据。
使用python,分析一个wav文件头部信息,处理音频数据。

思路:
open函数想以二进制模式打开文件,指定Mode参数为'b'。
二进制数据可以用readinto,读入提前分配的buffer中,便于数据处理。
解析二进制数据可以使用标准库中的struct模块的unpack方法

代码:

import struct
import array

# 以rb的模式打开wav文件
f = open('demo.wav','rb')

# 读取前44个字节
info = f.read(44)

# 将文件的指针移到文件的末尾
f.seek(0,2)

# 报告文件的指针
f.tell()

# 计算数组的长度
n = (f.tell() - 44) / 2

# 生成数组并初始化数组
buf = array.array('h',( 0 for _ in range(n)))

# 将文件的数据读入到buf中
# 首先要注意将文件的指针指向到data的开关
f.seek(44)
f.readinto(buf)

# 对每一个采样数据进行除以8的操作,这样可以使wav文件的声音变小一些
for i in range(n):
    buf[i] /= 8

# 将改变的数据写入新的文件中
f2 = open('demo2.wav','wb')
# 先写入文件信息头的部分
f2.write(info)
# 将文件的数据写入文件中
buf.tofile(f2)
# 关闭文件
f2.close()

=========================================================================

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

>>> info = f.read(44)

>>> info
b'RIFF\x06\xe5\xfa\x01WAVELIST\x98\x00\x00\x00INFOIART\x05\x00\x00\x00\xd9\xa9\xd9\xa9\x00\x00INAM\x03\x00'

>>> type(info)
bytes

>>> info[22:24]
b'FO'

>>> import struct

>>> struct.unpack('h',info[22:24])
(20294,)

>>> struct.unpack('>h',info[22:24])
(17999,)

>>> struct.unpack('>h',info[22:28])
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-99-f44c391af760> in <module>
----> 1 struct.unpack('>h',info[22:28])

error: unpack requires a buffer of 2 bytes

>>> struct.unpack('i',info[24:28])
(1414676809,)

>>> info.find(b'data')
-1

>>> f.seek(0)
0

>>> f.read(100)
b'RIFF\x06\xe5\xfa\x01WAVELIST\x98\x00\x00\x00INFOIART\x05\x00\x00\x00\xd9\xa9\xd9\xa9\x00\x00INAM\x03\x00\x00\x00\xbb\xad\x00\x00IPRD\x07\x00\x00\x00\xd4\xda\xd4\xb6\xb7\xbd\x00\x00IGNR\x05\x00\x00\x00\xc3\xf1\xd2\xa5\x00\x00ITOCJ\x00\x00\x00C+96+404D+A8'

>>> f.read(200)
b'2E+FD6E+15732+19F7F+1D6AB+22FBF+27F40+2C3B9+32D7B+37FEC+3D049\x00ITRK\x02\x00\x00\x006\x00fmt \x12\x00\x00\x00\x01\x00\x02\x00D\xac\x00\x00\x10\xb1\x02\x00\x04\x00\x10\x00\x00\x00data@\xe4\xfa\x01\x04\x00\xf4\xff\xfa\xff\x08\x00\x05\x00\xfc\xff\xfc\xff\x04\x00\x02\x00\xfa\xff\xff\xff\x07\x00\x03\x00\xfb\xff\xfd\xff\x08\x00\x06\x00\xfa\xff\xfa\xff\x05\x00\x02\x00\xf8\xff\x03\x00\x0c\x00\xfd\xff\xf3\xff\x01\x00\x0f\x00\x01\x00\xf4\xff\xff\xff\n\x00\x04\x00\xf7\xff\xf7\xff\x0f\x00\n\x00\xf0\xff\xf9\xff\t\x00\x06\x00\xfc\xff\xf8\xff\x05\x00\x00\x00\x00\x00\x00\x00'

>>> %load 5_2.py

>>> # %load 5_2.py
... import struct
... 
... def find_subchunk(f.name):
...     f.seek(12)
...     while True:
...         chunk_name = f.read(4)
...         chunk_size, = struct.unpack('i',f.read(4))
... 
...         if chunk_name == name:
...             return f.tell(),chunk_size
... 
...         f.seek(chunk_size,1) # 1代表指针的当前点,0代表开关,2代表文件结尾,这里是表示指针往前跳chunk_size个距离
... 
... 
  File "<ipython-input-106-4d5fb97742fd>", line 4
    def find_subchunk(f.name):
                       ^
SyntaxError: invalid syntax


>>> 

>>> %load 5_2.py

>>> # %load 5_2.py
... import struct
... 
... def find_subchunk(f,name):
...     f.seek(12)
...     while True:
...         chunk_name = f.read(4)
...         chunk_size, = struct.unpack('i',f.read(4))
... 
...         if chunk_name == name:
...             return f.tell(),chunk_size  # 此处返回的是指针的偏移量和要查找块的大小
... 
...         f.seek(chunk_size,1) # 1代表指针的当前点,0代表开关,2代表文件结尾,这里是表示指针往前跳chunk_size个距离
... 
... 

>>> f
<_io.BufferedReader name='demo.wav'>

>>> offset ,size = find_subchunk(f,b'data')  # 这里的数据前的部分不一定是44字节,需要定义一个函数来进行查找

>>> offset
206

>>> size
33219648

>>> offset + size   # 可以看到和ll查看的大小是一致的
33219854

>>> ll demo.wav
-rw-r--r-- 1 richardo 33219854 11月  3 15:42 demo.wav

>>> import numpy as np
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-115-0aa0b027fcb6> in <module>
----> 1 import numpy as np

ModuleNotFoundError: No module named 'numpy'

>>> import numpy as np

>>> buffer = np.zeros(size//2,dtype=np.short)  # 生成一个buffer在bufer进行数据的处理

>>> buf
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-118-470933492c55> in <module>
----> 1 buf

NameError: name 'buf' is not defined

>>> buffer
array([0, 0, 0, ..., 0, 0, 0], dtype=int16)

>>> f.readinto(buffer)  # 将data的数据部分全部读入buffer中
33219648

>>>  buffer
array([  4, -12,  -6, ...,  -2,  -6,   3], dtype=int16)

>>> buffer[10000:20000]
array([ 5, -3, -2, ..., -3, -6,  1], dtype=int16)

>>> buffer //= 8  # 进行除以8处理,可以使文件的音量降低

>>> f2 = open('out.wav','wb')

>>> f.seek(0)
0

>>> info = f.read(offset)

>>> f2.write(info)  # 先写data的前面头部部分
206

>>> buffer.tofile(f2)  # 再写data本身

>>> f2.close()

>>> 

posted @ 2020-07-12 22:25  Richardo-M-Lu  阅读(359)  评论(0编辑  收藏  举报