为什么附加二进制 pickle 不起作用?

3 投票
2 回答
7758 浏览
提问于 2025-04-15 22:51

我知道这不是pickle模块的正确用法,但我本以为这样做是可以的。我正在使用Python 3.1.2。

这是背景代码:

import pickle

FILEPATH='/tmp/tempfile'

class HistoryFile():
    """
    Persistent store of a history file  
    Each line should be a separate Python object
    Usually, pickle is used to make a file for each object,
        but here, I'm trying to use the append mode of writing a file to store a sequence
    """

    def validate(self, obj):
        """
        Returns whether or not obj is the right Pythonic object
        """
        return True

    def add(self, obj):
        if self.validate(obj):
            with open(FILEPATH, mode='ba') as f:    # appending, not writing
                f.write(pickle.dumps(obj))
        else:
            raise "Did not validate"

    def unpack(self):
        """
        Go through each line in the file and put each python object
        into a list, which is returned
        """
        lst = []
        with open(FILEPATH, mode='br') as f:
            # problem must be here, does it not step through the file?
            for l in f:
                lst.append(pickle.loads(l))
        return lst

现在,当我运行它时,它只打印出传递给类的第一个对象。

if __name__ == '__main__':

    L = HistoryFile()
    L.add('a')
    L.add('dfsdfs')
    L.add(['dfdkfjdf', 'errree', 'cvcvcxvx'])

    print(L.unpack())       # only prints the first item, 'a'!

这是因为它遇到了提前结束吗?也许追加操作只适用于ascii格式?(如果是这样,为什么让我用mode='ba'呢?)有没有更简单的方法可以做到这一点?

2 个回答

4

答案是,这确实可以工作,但如果在模式中不加上'+',那么通过打开文件时的追加功能自动添加的换行符会把二进制数据和字符串数据搞混(这是绝对不可以的)。所以要把这一行改成:

with open(FILEPATH, mode='ab') as f:    # appending, not writing
    f.write(pickle.dumps(obj))

改成

with open(FILEPATH, mode='a+b') as f:    # appending, not writing
    pickle.dump(obj, f)

Alex 还提到,为了更灵活,可以使用模式'r+b',但这需要适当的寻址。因为我想做一个历史文件,让它像先进后出那样处理一系列 Python 对象,所以我尝试在文件中追加对象是有道理的。只不过我之前没有正确地做到这一点 :)

其实不需要逐行读取文件,因为(当然啦!)它是序列化的。所以把:

for l in f:
    lst.append(pickle.loads(l))

替换成

while 1:
    try:
        lst.append(pickle.load(f))
    except IOError:
        break
6

你怎么会觉得把二进制的“腌制”文件拼接在一起会得到一个单一的“腌制”文件呢?其实,腌制的过程让你可以一个接一个地存放(和取回)多个项目,所以它显然是一种“自我结束”的序列化格式。别管那些行了,直接把它们取回来就行!举个例子:

>>> import pickle
>>> import cStringIO
>>> s = cStringIO.StringIO()
>>> pickle.dump(23, s)
>>> pickle.dump(45, s)
>>> s.seek(0)
>>> pickle.load(s)
23
>>> pickle.load(s)
45
>>> pickle.load(s)
Traceback (most recent call last):
   ...
EOFError
>>> 

只需要捕捉到 EOFError 错误,就能知道你什么时候完成了反腌制的过程。

撰写回答