Python使用pickle操作二进制文件报unpickling stack underflow错误如何解决
错误原因
你遇到的_pickle.UnpicklingError: unpickling stack underflow错误根本原因是pickle序列化后的内容是变长的,你在Update函数中直接原地覆盖记录的逻辑存在缺陷:
- 每次
pickle.dump()写入的字节长度由内容决定,不是固定值 - 当你修改记录后重新dump,如果新序列化的字节长度大于原位置的原有字节长度,就会覆盖下一条记录的头部内容,导致下一条记录变成不完整的pickle块,后续读取时就会抛出栈下溢错误
- 不同运行环境表现不同是因为测试时输入的内容长度有差异:PyCharm中测试时更新后的记录序列化长度刚好小于等于原长度,没有破坏后续记录;IDLE中测试时更新后的内容更长,破坏了后续记录结构,因此触发错误
修复方案
推荐使用「全量读取-内存修改-全量写回」的逻辑实现更新,完全规避变长序列化的原地写入问题,修改后的完整代码如下:
import pickle def Write(): f=open("StudentDetails.dat",'wb') while True: r=int(input("Enter Roll no.: ")) n=input("Enter Name: ") m=int(input("Enter Marks: ")) record=[r,n,m] pickle.dump(record,f) ch=input("Do you want to enter more records (Y/N)") if ch in 'Nn': break f.close() def Read(): f=open("StudentDetails.dat",'rb') try: while True: rec=pickle.load(f) print(rec) except EOFError: pass f.close() def Update(): # 先读所有记录到内存 records = [] f=open("StudentDetails.dat",'rb') try: while True: records.append(pickle.load(f)) except EOFError: pass f.close() # 内存中修改对应记录 rollno=int(input("Enter roll no whose marks you want to update: ")) updated = False for rec in records: if rec[0] == rollno: um=int(input("Enter updated marks: ")) rec[2] = um updated = True break if not updated: print("未找到对应学号的记录") return # 全量写回文件 f=open("StudentDetails.dat",'wb') for rec in records: pickle.dump(rec, f) f.close() Write() Read() Update() Read()
补充说明
如果你的学生记录量级非常大,不适合全量读入内存,可以改用固定长度的二进制存储格式(比如struct库序列化),才能安全实现原地更新操作。
内容的提问来源于stack exchange,提问作者Anshul Gupta
相关产品推荐
相关产品推荐

