如何读取损坏的pickle文件

问题描述 投票:0回答:1

我有一个使用 pickle.dump 编写的二进制文件,其中包含来自应用程序的日志(一组浮点数和字符串的元组),它在 24 小时内工作得很好,但现在当尝试使用

读取它时
import pickle

path = "/path/to/file.pkl"

with open(path, 'rb') as f:
    score_board = pickle.load(f)

我明白了

UnpicklingError: invalid load key, '\x00'.

我认为某个地方有一个空值损坏了文件,我知道错误出现在最后的输入中,因为发生错误时文件停止更新。集合中的每个元组包含(分数 [float]、短句 [str]、用户名 [str]、日期时间 [str])。我想知道是否有一种方法可以让我只打开文件直到某个点,甚至手动编辑它,以使其安全阅读。

提前致谢

python file pickle
1个回答
0
投票

我刚刚经历过这个。由于 stackoverflow 上没有任何有用的答案,所以我正在编写自己的答案。这是我修复它的方法。

TL;博士

将正确的操作码添加到 pickle 文件的末尾。我的应该以“bsbuu”结尾。但以“bsbj”(项目之间的操作码)结尾。编辑二进制文件以添加“bsbuu”。最后而不是“bsbj”修复了它。现在我的文件可以完美打开。吃吧,泡菜开发者。

您的操作码可能会有所不同,因为它取决于文件中的数据结构及其被切断的位置。使用相同的数据结构制作一个好的 pickle 文件,查看操作码,并根据需要调整损坏的文件。

您可以使用它来查看 pickle 文件中的操作码:

python -m pickletools file

您可以使用它将二进制 pickle 文件转换为纯文本十六进制,对其进行编辑,然后将其转回二进制:

xxd picklefile hexfile
(edit file)
xxd -r hexfile newpicklefile

泡菜格式

首先,让我们解决这个问题:泡菜很糟糕。这是一种糟糕的格式。恢复任何数据的能力为零的二进制垃圾。甚至所谓的 Python 专家也会说“糟糕的 pickle 文件?与你的数据说再见,然后重新开始。”这完全是一种格式的笑话。

Python 文档也好不到哪去。用于恢复部分数据的零工具。处理错误的能力为零。他们能做的最好的事情就是打印操作码并告诉您“编写您自己的 unpickler”。这种态度对用户不友好,连IBM都脸红了

我的情况

我的数据非常简单。这是一本古老的字典中的字典。像这样:

data = {
    "key1" : {
        "value1" : binarystring ,
        "value2" : binarystring ,
        "value3" : binarystring ,
    } ,
    "key2" : {
        "value1" : binarystring ,
        "value2" : binarystring ,
        "value3" : binarystring ,
    } ,
    ...
}

我的写作在快结束时被打断了。我的大部分数据仍然存在于损坏的 pickle 文件中。但愚蠢的东西打不开。 pickle.load 读取 80 MB 数据并显示“哎呀文件结束,抱歉没有数据给您”。这是垃圾。如果你传递一个 error 标志或其他东西,pickle 至少可以返回它成功读取的数据。不,泡菜拒绝。没有泡菜给你!!

我想恢复那里的数据。我不会编写自己的 unpickler 来找出所有这些操作码。这是一个愚蠢的解决方案,任何提出它的人都应该为自己感到羞耻。一定有更好的方法。

如何解决

我提出了一个更好的方法。 Pickle 文件在数据之间有一堆操作码来定义结构。您可以使用此命令来显示文件中的数据和操作码:

python -m pickletools file

我注意到在包含上述数据结构的示例pickle文件中,每个子字典(即“key1”下的字典)在下一个项目开始(即“key2”)之前以“bsbu”结尾。然后文件以“bsbuu”结尾。似乎是最后一个“u”。是一个操作码,意思是“结束(顶级)字典和结束数据”。

我的损坏文件以“bsbj”而不是“bsbuu”结尾。因此,如果我将文件的最后部分更改为“bsbuu”。相反,它应该关闭两个字典并结束数据。有道理吧?

我手头没有一个好的二进制编辑器,所以我用它来将二进制 pickle 文件转换为纯文本十六进制文件:

xxd picklefile hexfile

更改了最后的操作码,然后使用以下命令转换回二进制:

xxd -r hexfile newpicklefile

在新文件上调用 pickle.load,你瞧,Bob 是你的叔叔,它起作用了!我恢复了 80 MB 的数据。不用谢,蟒蛇!

 >:(

结论

是的,我知道我没有取回所有数据。但我得到了很多,这不感谢 python。告诉用户吃掉它并重新开始没有任何帮助。

将来我会考虑转向 json 来存储我的数据。这很痛苦,因为二进制字符串不能立即序列化为 json。我需要为 json.dump 和 json.load 制作一个转换器,可能将它们转换为十六进制字符串。这有点工作量。但恢复损坏的 json 文件比恢复 pickle 文件要容易得多。

是的,pickle 的功能远不止 json,它可以存储任意对象、可执行代码,yada yada yada。如果 pickle 不能正常工作,那有什么好处呢?如果一只小昆虫进入数据文件,泡菜就会彻底崩溃,尖叫着逃跑,拒绝回到房子里。来吧伙计。快点长大吧,蟒蛇。

在现实世界中,错误是会发生的。人和代码处理它们。默认的怪胎没问题。但不提供任何其他选择是不可原谅的。

© www.soinside.com 2019 - 2024. All rights reserved.