跳到主要内容

Python 二进制文件

在Python编程中,文件处理是一项基础而重要的技能。除了常见的文本文件外,二进制文件的处理也是实际应用中不可或缺的部分。二进制文件包含非文本数据,如图像、音频、视频或其他特定格式的数据,理解如何操作这些文件对于开发各类应用程序至关重要。

什么是二进制文件?

二进制文件与文本文件不同,它们存储的是原始的字节序列,而不是人类可读的字符。这些文件通常包含特定结构的数据,需要使用专门的程序或库来解释。

备注

文本文件与二进制文件的主要区别:

  • 文本文件存储的是字符,通常使用UTF-8等编码方式
  • 二进制文件存储的是原始字节,没有特定的字符编码

Python 中的二进制数据类型

在处理二进制文件之前,我们需要先了解Python中用于处理二进制数据的主要类型:

bytes对象

bytes是Python中不可变的字节序列,类似于字符串但专门用于表示字节数据。

python
# 创建bytes对象的几种方式
b1 = b'Hello' # 字节字面量
b2 = bytes([72, 101, 108, 108, 111]) # 从整数列表创建
b3 = bytes('Hello', 'utf-8') # 从字符串编码创建

print(b1) # 输出: b'Hello'
print(list(b1)) # 输出: [72, 101, 108, 108, 111]

输出:

b'Hello'
[72, 101, 108, 108, 111]

bytearray对象

bytearraybytes的可变版本,允许修改其内容。

python
# 创建和修改bytearray
ba = bytearray(b'Hello')
print(ba) # 输出: bytearray(b'Hello')

ba[0] = 74 # 修改第一个字节
print(ba) # 输出: bytearray(b'Jello')

输出:

bytearray(b'Hello')
bytearray(b'Jello')

二进制文件的读写操作

打开二进制文件

在Python中,通过在open()函数的模式参数中添加'b'来指定二进制模式:

python
# 打开二进制文件进行读取
with open('example.bin', 'rb') as file:
data = file.read()

# 打开二进制文件进行写入
with open('output.bin', 'wb') as file:
file.write(b'Binary data here')

读取二进制文件

python
# 读取整个二进制文件
with open('example.bin', 'rb') as file:
data = file.read()
print(type(data)) # 输出: <class 'bytes'>
print(len(data)) # 打印文件字节数

# 按块读取大型二进制文件
with open('large_file.bin', 'rb') as file:
chunk = file.read(1024) # 每次读取1KB
while chunk:
# 处理数据块
chunk = file.read(1024)

写入二进制文件

python
# 写入二进制数据
binary_data = bytes([0, 1, 2, 3, 4, 5])
with open('data.bin', 'wb') as file:
file.write(binary_data)

# 写入多个数据块
with open('multiple_chunks.bin', 'wb') as file:
file.write(b'First chunk')
file.write(b'Second chunk')

二进制文件操作实例

复制图像文件

下面是一个复制二进制图像文件的简单例子:

python
def copy_image(source, destination):
with open(source, 'rb') as src:
with open(destination, 'wb') as dst:
# 每次读取4KB数据
buffer_size = 4096
buffer = src.read(buffer_size)

while buffer:
dst.write(buffer)
buffer = src.read(buffer_size)

print(f"已成功将 {source} 复制到 {destination}")

# 使用函数复制图片
copy_image('original.jpg', 'copy.jpg')

创建简单的二进制文件格式

以下是如何创建一个简单的自定义二进制文件格式,存储一些整数和字符串:

python
import struct

def write_custom_binary(filename, numbers, text):
"""
写入自定义二进制格式文件
文件结构:
- 4字节: 整数数量
- 每个整数占4字节
- 4字节: 字符串长度
- N字节: UTF-8编码的字符串
"""
with open(filename, 'wb') as file:
# 写入整数数量
file.write(struct.pack('i', len(numbers)))

# 写入所有整数
for num in numbers:
file.write(struct.pack('i', num))

# 编码并写入字符串
encoded_text = text.encode('utf-8')
file.write(struct.pack('i', len(encoded_text)))
file.write(encoded_text)

def read_custom_binary(filename):
"""从自定义二进制文件中读取数据"""
with open(filename, 'rb') as file:
# 读取整数数量
count = struct.unpack('i', file.read(4))[0]

# 读取所有整数
numbers = []
for _ in range(count):
num = struct.unpack('i', file.read(4))[0]
numbers.append(num)

# 读取字符串
text_length = struct.unpack('i', file.read(4))[0]
text = file.read(text_length).decode('utf-8')

return numbers, text

# 写入示例数据
write_custom_binary('data.bin', [10, 20, 30, 40], "Hello Binary World!")

# 读取数据并验证
nums, txt = read_custom_binary('data.bin')
print(f"读取的数字: {nums}")
print(f"读取的文本: {txt}")

输出:

读取的数字: [10, 20, 30, 40]
读取的文本: Hello Binary World!

使用struct模块处理二进制数据

Python的struct模块提供了将Python值与C结构体表示的字节串互相转换的功能,这在处理二进制文件时非常有用。

python
import struct

# 将整数打包为4字节的二进制数据
packed_int = struct.pack('i', 42)
print(packed_int) # 输出: b'*\x00\x00\x00' (具体输出取决于系统的字节顺序)

# 解包二进制数据为整数
unpacked_int = struct.unpack('i', packed_int)[0]
print(unpacked_int) # 输出: 42

# 打包多个值
# 'i'表示4字节整数,'f'表示4字节浮点数,'5s'表示5个字符的字节串
packed_data = struct.pack('if5s', 10, 3.14, b'hello')
print(packed_data)

# 解包多个值
unpacked_data = struct.unpack('if5s', packed_data)
print(unpacked_data) # 输出: (10, 3.140000104904175, b'hello')

struct格式字符

以下是一些常用的格式字符:

格式C类型Python类型大小(字节)
cchar长度为1的bytes1
bsigned char整数1
Bunsigned char整数1
hshort整数2
Hunsigned short整数2
iint整数4
Iunsigned int整数4
qlong long整数8
Qunsigned long long整数8
ffloat浮点数4
ddouble浮点数8
schar[]bytes可变

实际应用案例

案例1:解析WAV音频文件头

WAV文件是一种常见的音频文件格式,其文件头包含重要的元数据。以下代码展示如何解析WAV文件头:

python
import struct
import wave

def print_wav_info(wav_file):
"""打印WAV文件的基本信息"""
with wave.open(wav_file, 'rb') as wav:
# 获取基本信息
n_channels = wav.getnchannels()
sample_width = wav.getsampwidth()
frame_rate = wav.getframerate()
n_frames = wav.getnframes()
compression_type = wav.getcomptype()
compression_name = wav.getcompname()

# 计算时长(秒)
duration = n_frames / frame_rate

# 打印信息
print(f"文件: {wav_file}")
print(f"通道数: {n_channels}")
print(f"采样宽度: {sample_width} 字节")
print(f"采样率: {frame_rate} Hz")
print(f"帧数: {n_frames}")
print(f"压缩类型: {compression_type}")
print(f"压缩名称: {compression_name}")
print(f"时长: {duration:.2f} 秒")

# 使用示例
try:
print_wav_info('example.wav') # 需要有一个WAV文件用于测试
except FileNotFoundError:
print("请替换为有效的WAV文件路径")

案例2:创建和读取简单的二进制数据库

下面是一个简单的二进制数据库实现,用于存储学生记录:

python
import struct
import os

class StudentRecord:
def __init__(self, id, name, age, score):
self.id = id
self.name = name
self.age = age
self.score = score

def __str__(self):
return f"ID: {self.id}, 姓名: {self.name}, 年龄: {self.age}, 成绩: {self.score}"

class BinaryStudentDB:
def __init__(self, filename):
self.filename = filename
# 如果文件不存在,创建一个空文件
if not os.path.exists(filename):
with open(filename, 'wb') as f:
# 写入记录数量(初始为0)
f.write(struct.pack('i', 0))

def add_student(self, student):
"""向数据库添加学生记录"""
with open(self.filename, 'r+b') as f:
# 读取当前记录数
f.seek(0)
record_count = struct.unpack('i', f.read(4))[0]

# 移动到文件末尾准备添加新记录
f.seek(0, 2) # 2表示从文件末尾计算偏移量

# 写入学生ID (4字节)
f.write(struct.pack('i', student.id))

# 写入姓名长度和姓名
name_bytes = student.name.encode('utf-8')
f.write(struct.pack('i', len(name_bytes)))
f.write(name_bytes)

# 写入年龄和成绩
f.write(struct.pack('if', student.age, student.score))

# 更新记录数
f.seek(0)
f.write(struct.pack('i', record_count + 1))

def get_all_students(self):
"""获取所有学生记录"""
students = []
with open(self.filename, 'rb') as f:
# 读取记录数
record_count = struct.unpack('i', f.read(4))[0]

for _ in range(record_count):
# 读取ID
student_id = struct.unpack('i', f.read(4))[0]

# 读取姓名
name_length = struct.unpack('i', f.read(4))[0]
name = f.read(name_length).decode('utf-8')

# 读取年龄和成绩
age, score = struct.unpack('if', f.read(8))

# 创建学生对象并添加到列表
students.append(StudentRecord(student_id, name, age, score))

return students

# 使用示例
db = BinaryStudentDB('students.bin')

# 添加一些学生记录
db.add_student(StudentRecord(1, "张三", 18, 90.5))
db.add_student(StudentRecord(2, "李四", 19, 85.0))
db.add_student(StudentRecord(3, "王五", 20, 92.5))

# 读取并打印所有学生记录
print("学生记录:")
for student in db.get_all_students():
print(student)

输出:

学生记录:
ID: 1, 姓名: 张三, 年龄: 18, 成绩: 90.5
ID: 2, 姓名: 李四, 年龄: 19, 成绩: 85.0
ID: 3, 姓名: 王五, 年龄: 20, 成绩: 92.5

总结

Python提供了强大而灵活的二进制文件处理能力,主要通过以下几个方面:

  1. 二进制数据类型bytesbytearray用于表示和操作字节序列
  2. 二进制文件I/O:通过在文件模式中添加'b'来实现二进制文件的读写
  3. 结构化二进制数据:使用struct模块可以轻松处理固定格式的二进制数据

掌握这些技能对于处理图像、音频、视频、网络协议实现以及自定义二进制格式的数据存储和传输都至关重要。

提示

在处理二进制数据时,务必要注意数据的字节顺序(大端序或小端序)以及数据的结构和对齐方式,这些因素可能影响到跨平台数据交换的正确性。

练习

  1. 创建一个程序,读取一个二进制图片文件并将其复制到一个新文件中。
  2. 实现一个函数,用于反转二进制文件的内容(即第一个字节变为最后一个,以此类推)。
  3. 设计一个简单的二进制文件格式来存储一系列的温度记录,每个记录包含日期、时间和温度值。
  4. 使用struct模块解析一个BMP图像文件的头部信息,提取图像的宽度、高度和色彩深度。
  5. 创建一个二进制文件加密工具,使用简单的XOR加密方法。

进一步学习资源

  • Python官方文档中关于二进制序列类型的介绍
  • Python官方文档中关于struct模块的详细说明
  • 了解更多关于常见二进制文件格式的规范,如WAV、BMP、PNG等
  • 探索第三方库如numpy,它提供了更高效的二进制数据处理能力