为什么读取空文件会导致"Pickle - EOFError: Ran out of input"错误?

228

在尝试使用Unpickler.load()时,我遇到了一个有趣的错误,以下是源代码:

open(target, 'a').close()
scores = {};
with open(target, "rb") as file:
    unpickler = pickle.Unpickler(file);
    scores = unpickler.load();
    if not isinstance(scores, dict):
        scores = {};

以下是追踪信息:

Traceback (most recent call last):
File "G:\python\pendu\user_test.py", line 3, in <module>:
    save_user_points("Magix", 30);
File "G:\python\pendu\user.py", line 22, in save_user_points:
    scores = unpickler.load();
EOFError: Ran out of input

我正在尝试读取的文件为空。 如何避免出现此错误,并获得一个空变量?


1
不要关闭文件。 - Alkesh Mahajan
2
第一行 open(...).close() 是为了确保文件存在。 - Magix
2
为什么不直接使用 os.path.isfile(target) 呢? - DLAN
3
以“为什么不只是”开头的评论真正错过了重点……人们提出问题是因为他们不知道。你应该详细说明。 - RobertJohnson
13个回答

343

这里大多数答案都涉及如何处理EOFError异常,如果您不确定pickled对象是否为空,这确实非常方便。

但是,如果您惊讶地发现pickle文件为空,可能是因为您以“wb”或其他可能覆盖文件的模式打开了文件名。

例如:

filename = 'cd.pkl'
with open(filename, 'wb') as f:
    classification_dict = pickle.load(f)

这将覆盖掉之前的序列化文件。您可能在使用以下命令之前错误地执行了此操作:


...
open(filename, 'rb') as f:

由于之前的代码覆盖了cd.pkl文件,因此出现了EOFError。

在Jupyter中工作或者在控制台(Spyder)中工作时,我通常会编写读/写代码的包装器,并随后调用该包装器。这样可以避免常见的读写错误,并且如果您将通过困境多次读取同一文件,则可以节省一些时间。


4
文件锁定的必要性 - 这个答案会帮助很多人,我在尝试读取正在被写入的文件。 - aspiring1
这对我有帮助。 - Pab
谢谢,伙计。这正是我所需要的。 - Youssef Snoussi
5
今天我也遇到了同样的问题,本来想读取文件,结果却用 'wb' 打开了它。有没有什么办法可以撤销这个操作? - Soma Siddhartha
1
有这个问题的人可能应该被引导到关于文件模式的规范之一。 - Karl Knechtel
显示剩余2条评论

183

我会先检查文件是否为空:

import os

scores = {} # scores is an empty dict already

if os.path.getsize(target) > 0:      
    with open(target, "rb") as f:
        unpickler = pickle.Unpickler(f)
        # if file is not empty scores will be equal
        # to the value unpickled
        scores = unpickler.load()

此外,你的代码中 open(target, 'a').close() 没有任何作用,而且你不需要使用 ;


open(target, 'a').close() 是为了确保文件存在 ;-) 我不需要使用;,但我刚从C语言过来,不在行末使用;会让我哭泣 T.T - Magix
好的,但是issinstance是不必要的,因为我想你只会挑选一个字典进行pickling,检查空文件就足够了。 - Padraic Cunningham
此外,检查文件不为空并不总意味着我可以反序列化它...引发异常...这就是为什么我认为你的答案虽然不错,但并不是最好的。 - Magix
2
捕获EOF异常并不能保护你免受其他潜在错误的影响。 - Padraic Cunningham
你说得对,这就是为什么我还会检查其他错误……仅捕获EOFError在这里是可以的,但捕获所有其他错误类型更好,并且以这种方式执行更聪明,因为我确信程序不会被词典读取函数停止,而这个函数在程序中并不那么重要。 - Magix
2
您可以使用os模块检查文件是否存在,这可能比每次打开和关闭文件更好。 - Padraic Cunningham

29

很有可能,腌制文件是空的。

如果您复制和粘贴代码,覆盖pickle文件非常容易。

例如,以下代码会写入一个pickle文件:

pickle.dump(df,open('df.p','wb'))

如果你复制了这段代码以重新打开文件,但是忘记将 'wb' 更改为 'rb',那么你将会覆盖该文件:

如果你复制了这段代码以重新打开文件,但是忘记将 'wb' 更改为 'rb',那么你会覆盖该文件:

df=pickle.load(open('df.p','wb'))

正确的语法是

df=pickle.load(open('df.p','rb'))

1
最后两个代码示例应该交换,对吗? - Daniello
是的,我犯了同样的错误,所有的结果都被毁了,不得不重新运行之前的所有计算,并等待一天才能得到结果。真可惜! - sikisis
它不能在Python 3.5上运行。 - Shadab K
@ShadabK 我使用的是3.7.X版本,一切正常。 - user2723494

11

就像您看到的那样,那实际上是一种自然错误..

从Unpickler对象读取的典型构造将类似于以下内容..

try:
    data = unpickler.load()
except EOFError:
    data = list()  # or whatever you want

EOFError 表示读取空文件时抛出的异常,它的意思是文件结束


5

我也遇到过同样的问题。原来在写入pickle文件时,没有使用file.close(),加上这行代码后错误就消失了。


这并没有真正回答问题。如果您有不同的问题,可以通过点击提问来提出。如果您想在此问题获得新的答案时得到通知,您可以关注此问题。一旦您拥有足够的声望,您还可以添加悬赏以吸引更多关注。- 来自审核 - Sunderam Dubey
1
我喜欢这个答案。我遇到了同样的错误,一直在想方设法弄清楚这个问题如何适用于我的情况。但是它并不适用于我,但是这个答案确实很有用,至少让我找对了方向。 - MikeB2019x

3
if path.exists(Score_file):
      try : 
         with open(Score_file , "rb") as prev_Scr:

            return Unpickler(prev_Scr).load()

    except EOFError : 

        return dict() 

2
你好,欢迎来到Stackoverflow。你能解释一下这段代码吗? - Alexander

3
你可以捕获该异常并从那里返回任何你想要的内容。
open(target, 'a').close()
scores = {};
try:
    with open(target, "rb") as file:
        unpickler = pickle.Unpickler(file);
        scores = unpickler.load();
        if not isinstance(scores, dict):
            scores = {};
except EOFError:
    return {}

13
问题在于它会默默隐藏损坏的文件。 - Ross Ridge

3
我遇到过这个错误很多次,原因是在写入文件后没有关闭它。如果我们不关闭文件,则内容仍然留在缓冲区中,文件保持为空。 要将内容保存到文件中,要么关闭该文件,要么使文件对象超出范围。
这就是为什么在加载时会出现“ran out of input”错误,因为文件为空。所以你有两个选择:
  1. file_object.close()
  2. file_object.flush():如果你不想在程序中间关闭文件,则可以使用flush()函数,因为它会强制将内容从缓冲区移到文件中。

3
同样在我的情况下,我从Jupyter笔记本的执行中看到,如果序列化对象的大小对于内存来说太大,它会引发相同的错误。 - serpiko
with open语法应该可以防止这种情况发生,对吗? - undefined

1

当您的pickle文件为空(0字节)时,会出现此错误。您需要先检查pickle文件的大小。在我的情况下,就是这种情况。希望这能帮到您!


0
temp_model = os.path.join(models_dir, train_type + '_' + part + '_' + str(pc))
# print(type(temp_model)) # <class 'str'>
filehandler = open(temp_model, "rb")
# print(type(filehandler)) # <class '_io.BufferedReader'>
try:
    pdm_temp = pickle.load(filehandler)
except UnicodeDecodeError:
    pdm_temp = pickle.load(filehandler, fix_imports=True, encoding="latin1")

pdm_temp = pickle.load(filehandler) EOFError: 输入流已耗尽 - 郝大为
我有一个问题想问你。文件 pdm_temp = pickle.load(file handler) 报错 EOFError: Ran out of input。 - 郝大为

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接