如何正确unpickle类对象?解决pickle仅读取首行数据问题
问题描述
当前代码除unpickle反序列化数据环节外均可正常运行:程序从.txt文件逐行读取内容,每行包含5个变量,原本用于赋值给players类的实例对象。但执行unpickle操作时,仅能读取到.txt文件首行对应的pickle数据,无法获取全部内容。
已尝试多种pickle数据写入方式,判断问题出在pickle写入逻辑中。
相关代码
读取txt文件并执行pickle写入的代码段
import pickle import re class players: def __init__(players,lastName,firstName,games,total,position): players.lastName = lastName players.firstName = firstName players.games = games players.total = total players.position = position # Transfer Team Details def transferDetails(): exists = False while exists == False: try: file = input("\nPlease enter the text file name: ") text_file = open(file + ".txt", "r") binary_File = open(file + ".dat", "wb") exists = True for line in text_file.readlines(): lastName,firstName,games,total,position = [str(i.strip()) for i in line.split(',')] # Assign here lastName = lastName[0:20]# Truncate lastName = re.sub(r'[^a-zA-Z]', '', lastName)# Cleaning firstName = firstName[0:20]# Truncate firstName = re.sub(r'[^a-zA-Z]', '', firstName)# Cleaning # Changing position to default if not set correctly if position != "Forward" and position != "Centre" and position != "Guard" : position = "Guard" ### Changing games to valid number: 0-200 games = float(games) if games < 0 : games = 0 elif games > 200 : games = 0 ### Changing total points value to valid number: 0-1000 total = float(total)### if total < 0 : total = 0 elif total > 1000 : total = 0 pickle.dump(lastName, binary_File) pickle.dump(firstName, binary_File) pickle.dump(games, binary_File) pickle.dump(total, binary_File) pickle.dump(position, binary_File) binary_File.close ###TESTING ONLY### print("Last Name: ", lastName) print("First Name : ", firstName) print("Games played: ", games) print("Total points: ", total) print("Position: ", position) print("\n\n") ###TESTING ONLY### ### File not found except FileNotFoundError: print("File not found")
unpickle读取数据的代码段
def listPlayers(): #Input Code Here file2 = input("\nPlease enter the binary file name: ") with open(file2 + ".dat", 'rb') as binary_File: lastName = pickle.load(binary_File) firstName = pickle.load(binary_File) games = pickle.load(binary_File) total = pickle.load(binary_File) position = pickle.load(binary_File) print(lastName) print(firstName) print(games) print(total) print(position)
运行异常表现
原始.txt文件共包含4行球员数据,对应4个独立的球员对象,但实际执行unpickle函数仅输出1条球员记录:
Please enter the binary file name: test1 bob billy 10.0 40.0 Guard
问题原因与修复方法
核心错误点
- 写入逻辑存在两处错误:
- 文件关闭语句
binary_File.close未添加括号,不会实际执行文件关闭操作,写入缓冲区中除第一行外的剩余数据不会被刷入磁盘,后续行数据实际没有存入.dat文件 - 文件关闭语句被错误放在逐行读取的
for循环内部,即便补充括号调用关闭方法,第一次循环写完第一行数据后文件就会被关闭,后续行写入会直接报错
- 文件关闭语句
- 读取逻辑存在一处错误:
仅执行了5次pickle.load()读取第一组共5个字段,没有循环读取文件内所有序列化内容,即便写入的文件数据完整,也只能拿到第一条记录
修复后代码
写入逻辑修复
推荐使用with上下文管理器自动处理文件关闭,不需要手动写close,同时可以直接序列化players类实例,不需要分开dump5个字段,代码更简洁:
def transferDetails(): while True: try: file = input("\nPlease enter the text file name: ") # 自动管理两个文件的关闭 with open(file + ".txt", "r") as text_file, open(file + ".dat", "wb") as binary_File: for line in text_file.readlines(): # 跳过空行避免报错 if not line.strip(): continue lastName,firstName,games,total,position = [i.strip() for i in line.split(',')] lastName = re.sub(r'[^a-zA-Z]', '', lastName[0:20]) firstName = re.sub(r'[^a-zA-Z]', '', firstName[0:20]) if position not in ("Forward", "Centre", "Guard"): position = "Guard" # 数值校验,增加非数值输入兼容 try: games = float(games) except: games = 0 games = 0 if games <0 or games>200 else games try: total = float(total) except: total =0 total =0 if total <0 or total>1000 else total # 直接序列化players实例 player = players(lastName, firstName, games, total, position) pickle.dump(player, binary_File) # 测试打印 print(f"Last Name: {lastName}\nFirst Name: {firstName}\nGames played: {games}\nTotal points: {total}\nPosition: {position}\n\n") break except FileNotFoundError: print("File not found")
读取逻辑修复
循环执行pickle.load(),直到捕获到文件结束抛出的EOFError时停止读取,即可拿到所有球员数据:
def listPlayers(): file2 = input("\nPlease enter the binary file name: ") player_list = [] with open(file2 + ".dat", 'rb') as binary_File: while True: try: player = pickle.load(binary_File) player_list.append(player) # 打印球员信息 print(player.lastName) print(player.firstName) print(player.games) print(player.total) print(player.position) print("---") except EOFError: # 读取到文件末尾,退出循环 break print(f"共读取到{len(player_list)}条球员记录")
注意:如果不想序列化类实例,保持原有分开dump5个字段的写法,读取时每次循环连续load5个字段即可,逻辑和上述读取实例的方式一致,直到捕获EOFError停止。
内容的提问来源于stack exchange,提问作者Emilio
相关产品推荐
相关产品推荐

