【问题标题】:If pickling was interrupted, will unpickling necessarily always fail? - Python如果酸洗被中断,解酸必然总是失败吗? - Python
【发布时间】:2015-07-15 02:43:52
【问题描述】:

假设由于崩溃,我将 pickle 对象写入磁盘的尝试未完成。尝试解除对象 总是 是否会导致异常,或者写出的片段可能会被解释为有效的 pickle 并且错误不会被注意到?

【问题讨论】:

  • 如果发生崩溃,正如有人指出的那样,您甚至不会看到返回的 pickle 字符串。你的意思是,“假设我腌制一个对象,然后开始将腌制写入磁盘并且发生崩溃......”?
  • 看,我直接告诉你。如果酸洗失败,最好把那桶黄瓜扔掉。试图将它们从酸洗汁中取出并以其他方式重新使用它们只会以泪水告终。
  • @Adam:“我不担心恢复数据,但错误可能会被忽视。” (Casebash)

标签: python


【解决方案1】:

与提供的其他答案相反,我相信我们可以就泡菜的可回收性提出强有力的论据。答案是:“是的,不完整的泡菜总是会导致异常。”

为什么我们能够做到这一点?因为“pickle”格式实际上是一种基于堆栈的小型语言。在基于堆栈的语言中,您编写的代码将一项接一项地推送到堆栈上,然后调用一个操作符来处理您积累的数据。碰巧pickle 必须以命令“.”结尾,该命令表示:“现在将位于堆栈底部的项目作为此pickle 的值返回。”如果你的泡菜被提前切掉,它不会以这个命令结束,你会得到一个 EOF 错误。

如果您想尝试恢复一些数据,您可能必须编写自己的解释器,或者在某个地方调用 pickle.py,以便在完成解释堆栈但没有找到“.”时引发 EOFError。要记住的主要事情是,与大多数基于堆栈的语言一样,大数据结构是“向后”构建的:首先您将大量小字符串或数字放在堆栈上,然后您调用一个说“将它们放在一个列表中”或“抓取堆栈上的项目对并制作字典”的操作。因此,如果泡菜被打断,您会发现堆栈中充满了将要构建的对象的碎片,但是您会丢失告诉您 什么 将要执行的最终代码从碎片中构建。

【讨论】:

  • 这是一个非常有趣的答案,但我想我需要进一步澄清这个问题。我不担心恢复数据,但错误可能会被忽视。
  • EOFError 将 (a) 总是在 pickle 被剪短时生成,并且 (b) 不会是你会错过的错误,除非你故意使用 "try: except:" 来捕捉它。 :-) 所以不需要什么花哨的东西:泡菜已经是安全的,并且自动检测它们是否已被截断并引发错误。
  • +1 有趣。这是该问题的完美答案,如果它经得起测试,是一个明确且“按定义”的答案。
【解决方案2】:

这是 S. Lott 的答案的发展,我的建议是:将哈希或校验和附加到您的数据中,在再次取消腌制之前检查。

这是 safepickle/safeunpickle 的(简单)实现,展示如何使用哈希(在本例中为加密强哈希)填充腌制数据:

import hashlib
import cPickle as pickle

_HASHLEN = 20

def safepickle(obj):
    s = pickle.dumps(obj)
    s += hashlib.sha1(s).digest()
    return s

def safeunpickle(pstr):
    data, checksum = pstr[:-_HASHLEN], pstr[-_HASHLEN:]
    if hashlib.sha1(data).digest() != checksum:
        raise ValueError("Pickle hash does not match!")
    return pickle.loads(data)


l = range(20)
p = safepickle(l)
new_l = safeunpickle(p)
print new_l == l

这种方法是为了确保你 unpickle 的内容与你之前 pickle 和写入磁盘的内容相匹配,但它当然不能防止混合不同的 pickle 或恶意攻击。

(此方法可以推广到模式safe_write_filesafe_read_file 用于任何整文件数据。)

【讨论】:

  • 这提供了额外的保护以防止其他形式的损坏。我想如果我只想确保写完整的泡菜,我可以先写出泡菜的长度。
  • 虽然这不能回答所提出的确切问题,但我会接受。实际的答案可能是相当技术性的,也许 Python 邮件列表会是一个更好的提问地点。
【解决方案3】:

腌制一个对象会返回一个 str 对象,或者将一个 str 对象写入文件……它不会修改原始对象。如果在酸洗调用中发生“崩溃”(异常),结果将不会返回给调用者,因此您没有任何可以尝试取消酸洗的东西。再说了,你为什么要解开一些异常后留下的无用垃圾?

【讨论】:

    【解决方案4】:

    我怀疑您是否可以声称它总是会导致异常。 Pickles 实际上是用专门的堆栈语言编写的程序。 pickle 的内部细节因版本而异,偶尔会添加新的 pickle 协议。崩溃后 pickle 的状态,以及由此产生的对 unpickler 的影响,很难用一个简单的语句来概括,比如“它总是会导致异常”。

    【讨论】:

    • 你说得对,这在很大程度上取决于我没有详细信息的实现。
    【解决方案5】:

    为了确保你有一个“完整的”pickle 文件,你需要 pickle 三个东西。

    1. 腌制某种类型的标头,该标头声明有多少对象以及文件结束标志的外观。例如,整数和 EOF 字符串的元组。

    2. 腌制你真正关心的对象。计数由标题给出。

    3. 腌制一个您实际上并不关心的尾部对象,但它仅与标题中的声明相匹配。这可以只是一个与标头中的内容匹配的字符串。

    当你 unpickle 这个文件时,你必须 unpickle 三个东西:

    1. 标题。你关心尾巴的数量和形状。

    2. 你真正关心的对象。

    3. 尾部对象。检查它是否与标题匹配。除此之外,它并没有传达太多信息,只是文件是完整编写的。

    【讨论】:

    • 很遗憾,其实我并不知道提前写了多少个对象。
    • @Casebash:可以跳过header,tail可以是前面pickle数据的hash。此哈希不必腌制,您可以在之后附加它,并在 unpickling 之前将其剥离(在验证之后)。
    • 这会告诉我(很有可能)整个过程是否完成,但我想在每一步都知道这一点。我想可以将哈希保存在一个单独的文件中,该文件记录添加的最后一个泡菜的哈希值。
    • 正确。重点是这个。为确保您有一个完全腌制的对象,您必须腌制至少 2 个对象。你想要的,至少还有一个“一切正常”的对象。
    猜你喜欢
    • 2010-12-30
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 2014-09-15
    • 1970-01-01
    • 2012-05-03
    • 2011-04-01
    • 1970-01-01
    相关资源
    最近更新 更多