第83集:二进制文件操作
学习目标
- 理解二进制文件与文本文件的区别
- 掌握二进制文件的读写操作
- 学会使用struct模块处理二进制数据
- 掌握图片、音频等二进制文件的处理
- 了解二进制文件的常见应用场景
一、二进制文件概述
1.1 什么是二进制文件
二进制文件是以二进制形式存储数据的文件,与文本文件不同,二进制文件的内容不是人类可读的文本,而是计算机可以直接处理的二进制数据。
1.2 二进制文件与文本文件的区别
| 特性 | 文本文件 | 二进制文件 |
|---|---|---|
| 存储方式 | 字符编码(如UTF-8) | 原始二进制数据 |
| 可读性 | 人类可读 | 人类不可读 |
| 文件大小 | 较大(编码开销) | 较小(紧凑存储) |
| 处理方式 | 按字符处理 | 按字节处理 |
| 应用场景 | 配置文件、日志等 | 图片、音频、视频等 |
1.3 常见二进制文件类型
- 图片文件:PNG、JPG、GIF、BMP等
- 音频文件:MP3、WAV、OGG等
- 视频文件:MP4、AVI、MKV等
- 压缩文件:ZIP、RAR、7Z等
- 可执行文件:EXE、DLL等
- 数据文件:数据库文件、序列化数据等
二、打开二进制文件
2.1 二进制打开模式
在文本模式的基础上添加'b'字符即可打开二进制模式:
| 模式 | 描述 |
|---|---|
| 'rb' | 二进制只读 |
| 'wb' | 二进制写入(覆盖) |
| 'ab' | 二进制追加 |
| 'rb+' | 二进制读写 |
| 'wb+' | 二进制读写(覆盖) |
| 'ab+' | 二进制读写(追加) |
2.2 打开二进制文件示例
# 读取二进制文件
with open('image.png', 'rb') as f:
data = f.read()
# 写入二进制文件
with open('output.bin', 'wb') as f:
f.write(b'\x00\x01\x02\x03')三、读取二进制文件
3.1 读取整个文件
with open('file.bin', 'rb') as f:
data = f.read()
print(type(data)) # <class 'bytes'>3.2 读取指定字节数
with open('file.bin', 'rb') as f:
# 读取前100字节
chunk = f.read(100)
print(chunk)3.3 逐字节读取
with open('file.bin', 'rb') as f:
while True:
byte = f.read(1)
if not byte:
break
print(byte)四、写入二进制文件
4.1 写入字节串
# 写入字节串
data = b'Hello, Binary!'
with open('output.bin', 'wb') as f:
f.write(data)4.2 写入单个字节
with open('output.bin', 'wb') as f:
f.write(b'\x00')
f.write(b'\x01')
f.write(b'\x02')4.3 使用bytes()函数
# 从列表创建字节串
data = bytes([0x00, 0x01, 0x02, 0x03])
with open('output.bin', 'wb') as f:
f.write(data)4.4 使用bytearray()
# 创建可变的字节数组
data = bytearray([0x00, 0x01, 0x02, 0x03])
data.append(0x04)
with open('output.bin', 'wb') as f:
f.write(data)五、struct模块
5.1 struct模块简介
struct模块提供了在Python值和C结构体之间转换的功能,可以方便地处理二进制数据。
5.2 常用格式字符
| 格式字符 | C类型 | Python类型 | 大小(字节) |
|---|---|---|---|
| 'b' | signed char | int | 1 |
| 'B' | unsigned char | int | 1 |
| 'h' | short | int | 2 |
| 'H' | unsigned short | int | 2 |
| 'i' | int | int | 4 |
| 'I' | unsigned int | int | 4 |
| 'f' | float | float | 4 |
| 'd' | double | float | 8 |
| 's' | char[] | bytes | - |
5.3 pack()函数
将Python值打包成二进制数据。
import struct
# 打包一个整数和一个浮点数
data = struct.pack('if', 42, 3.14)
print(data) # b'*\x00\x00\x00\x1f\x85\xebQ'5.4 unpack()函数
将二进制数据解包成Python值。
import struct
data = b'*\x00\x00\x00\x1f\x85\xebQ'
values = struct.unpack('if', data)
print(values) # (42, 3.140000104904175)5.5 calcsize()函数
计算格式字符串对应的大小。
import struct
size = struct.calcsize('if')
print(size) # 8 (4字节int + 4字节float)六、处理图片文件
6.1 读取图片文件
# 读取图片文件
with open('image.png', 'rb') as f:
image_data = f.read()
print(f"图片大小:{len(image_data)} 字节")6.2 复制图片文件
# 复制图片文件
def copy_image(src, dst):
with open(src, 'rb') as src_file:
with open(dst, 'wb') as dst_file:
dst_file.write(src_file.read())
copy_image('source.png', 'copy.png')6.3 读取图片头部信息
# 读取PNG文件头部信息
def read_png_header(filename):
with open(filename, 'rb') as f:
header = f.read(8)
if header[:8] == b'\x89PNG\r\n\x1a\n':
print("这是一个PNG文件")
# 读取IHDR chunk
chunk_length = struct.unpack('>I', f.read(4))[0]
chunk_type = f.read(4)
print(f"Chunk类型:{chunk_type}")
else:
print("这不是PNG文件")
read_png_header('image.png')七、处理音频文件
7.1 读取WAV文件头部
import struct
def read_wav_header(filename):
with open(filename, 'rb') as f:
# 读取RIFF头
riff = f.read(4)
file_size = struct.unpack('<I', f.read(4))[0]
wave = f.read(4)
# 读取fmt子块
fmt = f.read(4)
fmt_size = struct.unpack('<I', f.read(4))[0]
audio_format = struct.unpack('<H', f.read(2))[0]
num_channels = struct.unpack('<H', f.read(2))[0]
sample_rate = struct.unpack('<I', f.read(4))[0]
byte_rate = struct.unpack('<I', f.read(4))[0]
block_align = struct.unpack('<H', f.read(2))[0]
bits_per_sample = struct.unpack('<H', f.read(2))[0]
print(f"音频格式:{audio_format}")
print(f"声道数:{num_channels}")
print(f"采样率:{sample_rate} Hz")
print(f"位深度:{bits_per_sample} bit")
read_wav_header('audio.wav')八、二进制文件应用场景
8.1 数据序列化
import struct
# 将数据序列化为二进制
def serialize_data(data):
# 格式:3个整数,2个浮点数,1个字符串(10字节)
packed = struct.pack('3if10s',
data['a'], data['b'], data['c'],
data['x'], data['y'],
data['name'].encode('utf-8'))
return packed
# 反序列化
def deserialize_data(packed):
values = struct.unpack('3if10s', packed)
return {
'a': values[0],
'b': values[1],
'c': values[2],
'x': values[3],
'y': values[4],
'name': values[5].decode('utf-8').rstrip('\x00')
}
data = {'a': 1, 'b': 2, 'c': 3, 'x': 1.5, 'y': 2.5, 'name': 'test'}
packed = serialize_data(data)
unpacked = deserialize_data(packed)8.2 网络数据传输
import struct
# 构造网络数据包
def build_packet(seq_num, data):
# 数据包格式:序列号(4字节) + 数据长度(4字节) + 数据
packet = struct.pack('!II', seq_num, len(data))
packet += data.encode('utf-8')
return packet
# 解析网络数据包
def parse_packet(packet):
seq_num = struct.unpack('!I', packet[:4])[0]
data_len = struct.unpack('!I', packet[4:8])[0]
data = packet[8:8+data_len].decode('utf-8')
return {'seq_num': seq_num, 'data': data}8.3 文件加密
# 简单的XOR加密
def xor_encrypt(data, key):
encrypted = bytearray()
for byte in data:
encrypted.append(byte ^ key)
return bytes(encrypted)
# 加密文件
def encrypt_file(input_file, output_file, key):
with open(input_file, 'rb') as f:
data = f.read()
encrypted = xor_encrypt(data, key)
with open(output_file, 'wb') as f:
f.write(encrypted)
# 解密文件
def decrypt_file(input_file, output_file, key):
with open(input_file, 'rb') as f:
data = f.read()
decrypted = xor_encrypt(data, key)
with open(output_file, 'wb') as f:
f.write(decrypted)九、二进制文件处理技巧
9.1 分块读取大文件
def process_large_file(filename, chunk_size=4096):
with open(filename, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
# 处理每个块
process(chunk)9.2 查找二进制模式
def find_pattern(filename, pattern):
with open(filename, 'rb') as f:
data = f.read()
index = data.find(pattern)
if index != -1:
print(f"找到模式,位置:{index}")
else:
print("未找到模式")
find_pattern('file.bin', b'\x00\x01\x02')9.3 修改二进制文件
def modify_file(filename, offset, new_data):
with open(filename, 'r+b') as f:
f.seek(offset)
f.write(new_data)
# 修改文件的第10个字节
modify_file('file.bin', 10, b'\xFF')十、最佳实践
10.1 使用with语句
# 推荐
with open('file.bin', 'rb') as f:
data = f.read()
# 不推荐
f = open('file.bin', 'rb')
data = f.read()
f.close()10.2 处理大文件时分块读取
# 推荐
with open('large_file.bin', 'rb') as f:
while True:
chunk = f.read(4096)
if not chunk:
break
process(chunk)
# 不推荐(可能内存溢出)
with open('large_file.bin', 'rb') as f:
data = f.read() # 一次性读取整个文件10.3 使用struct模块处理结构化数据
import struct
# 推荐:使用struct模块
data = struct.pack('if', 42, 3.14)
# 不推荐:手动构造
data = bytes([42, 0, 0, 0]) + bytes([0x1f, 0x85, 0xeb, 0x51])10.4 异常处理
try:
with open('file.bin', 'rb') as f:
data = f.read()
except FileNotFoundError:
print('文件不存在')
except PermissionError:
print('没有权限访问文件')
except Exception as e:
print(f'发生错误:{e}')十一、总结
本集学习了二进制文件操作的知识:
- 二进制文件与文本文件的区别
- 二进制文件的打开、读取、写入操作
- struct模块的使用方法
- 图片、音频等二进制文件的处理
- 二进制文件的应用场景和处理技巧
二进制文件操作在处理图片、音频、视频、网络数据传输等场景中非常重要。下一集我们将学习CSV文件操作。
十二、练习题
- 创建一个二进制文件,写入一些字节,然后读取并显示。
- 使用struct模块打包和解包一组数据(整数、浮点数、字符串)。
- 编写一个函数,复制图片文件。
- 实现一个简单的XOR加密/解密程序。
- 读取WAV文件的头部信息,显示采样率、声道数等。
- 编写一个函数,在二进制文件中查找指定的字节模式。
- 实现一个数据序列化/反序列化程序。
- 编写一个程序,分块读取大文件并统计文件大小。