使用 pickle
模块读写二进制文件
在 Python 中,读取和写入二进制文件(.dat
或 .pickle
文件扩展名)的协议已在内置的 pickle
模块中实现。
pickle
是 Python 特定的二进制文件格式,它不仅可以用于存储二进制数据,还可以存储任何 Python 对象。
将数据结构(列表、字典等)和代码对象(类、函数等)转换为可以存储在二进制文件中的字节的过程称为序列化 (Serialization) 或 pickling。此二进制文件可以存储在磁盘上或共享,并且以后可以通过 Python 反序列化 (deserialization 或 unpickling) 并使用。
存储数据 (Dumping Data / Pickling)
dump()
函数可用于将任何数据或对象的 pickled 表示形式写入文件。
语法:pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
其中:
obj
是要写入的对象file
是一个打开的文件对象(以二进制写入wb
或追加ab
模式打开)。protocol
是可选的,指定使用的 pickle 协议版本。
>>> import pickle
>>> l = [["Anita","Maths",83],
... ["Amar","Maths",95],
... ["Ani","Maths",90]]
>>> with open("marks.dat", "wb") as f: # 使用 with 确保关闭
... pickle.dump(l, f)
...
>>>
加载数据 (Loading Data / Unpickling)
load()
函数可用于从文件中读取对象的 pickled 表示形式。
语法:pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
其中 file
是一个打开的文件对象(以二进制读取模式 rb
打开)。
>>> import pickle
>>> try:
... with open("marks.dat", "rb") as f:
... l = pickle.load(f)
... print(l)
... except FileNotFoundError:
... print("文件未找到")
...
[['Anita', 'Maths', 83], ['Amar', 'Maths', 95], ['Ani', 'Maths', 90]]
安全警告: pickle
模块不安全。它很容易被构造恶意 pickle 数据来在解封 (unpickling) 过程中执行任意代码。永远不要解封来自不受信任或未经验证来源的数据。
示例:遍历二进制文件
编写一个程序,执行以下记录保存活动:
- 接受一些学生成绩作为输入,并将数据写入二进制文件
- 从文件中读取所有数据
- 向文件中添加更多数据
- 显示文件的全部内容
代码
import pickle
#写入初始数据
try:
with open("marks.dat", "wb") as f:
flag = 1
while flag != 0:
name = input("输入学生姓名: ")
subject = input("输入科目: ")
marks = float(input("输入分数: "))
rec = [name, subject, marks]
pickle.dump(rec, f) # 一次写入一个记录
try:
flag = int(input("输入 1 继续添加,输入 0 终止: "))
except ValueError:
flag = 0 # 输入无效时终止
except IOError as e:
print(f"写入文件时出错: {e}")
#读取文件内容并显示
print("\n文件当前内容:")
records = [] # 用于存储所有记录的列表
try:
with open("marks.dat", "rb") as f:
while True: # 循环读取直到文件末尾
try:
rec = pickle.load(f)
records.append(rec)
print(rec)
except EOFError: # 到达文件末尾时中断循环
print("已到达文件末尾")
break
except FileNotFoundError:
print("文件 'marks.dat' 未找到。")
except IOError as e:
print(f"读取文件时出错: {e}")
except pickle.UnpicklingError as e:
print(f"解封数据时出错: {e}")
#添加更多数据
print("\n添加更多数据...")
try:
with open("marks.dat", "ab") as f: # 使用 'ab' 追加模式
flag = 1
while flag != 0:
name = input("输入学生姓名: ")
subject = input("输入科目: ")
marks = float(input("输入分数: "))
rec = [name, subject, marks]
pickle.dump(rec, f)
try:
flag = int(input("输入 1 继续添加,输入 0 终止: "))
except ValueError:
flag = 0
except IOError as e:
print(f"追加写入文件时出错: {e}")
#显示追加后的所有内容
print("\n追加数据后的文件内容:")
all_records_after_append = []
try:
with open("marks.dat", "rb") as f:
while True:
try:
rec = pickle.load(f)
all_records_after_append.append(rec)
print(rec)
except EOFError:
print("已到达文件末尾")
break
except FileNotFoundError:
print("文件 'marks.dat' 未找到。")
except IOError as e:
print(f"读取文件时出错: {e}")
except pickle.UnpicklingError as e:
print(f"解封数据时出错: {e}")
(注意: 原代码在读取和写入时混合,且读取逻辑需要改进以正确处理多个 pickle 对象。这里改进了逻辑,分离读写,并使用循环和异常处理来读取所有对象直到 EOFError。同时添加了基本的错误处理。)
输入/输出示例
输入学生姓名: Anita
输入科目: Maths
输入分数: 83
输入 1 继续添加,输入 0 终止: 1
输入学生姓名: Amar
输入科目: Maths
输入分数: 95
输入 1 继续添加,输入 0 终止: 0
文件当前内容:
['Anita', 'Maths', 83.0]
['Amar', 'Maths', 95.0]
已到达文件末尾
添加更多数据...
输入学生姓名: Akash
输入科目: Science
输入分数: 92
输入 1 继续添加,输入 0 终止: 1
输入学生姓名: Ira
输入科目: Science
输入分数: 99
输入 1 继续添加,输入 0 终止: 0
追加数据后的文件内容:
['Anita', 'Maths', 83.0]
['Amar', 'Maths', 95.0]
['Akash', 'Science', 92.0]
['Ira', 'Science', 99.0]
已到达文件末尾