从zip文件中加载pickle文件

6
由于某些原因,我无法在ZipFile.open()返回的文件类型对象上使用cPickle.load。如果我在由ZipFile.open()返回的文件类型对象上调用read(),则可以使用cPickle.loads。
示例...
import zipfile
import cPickle

# the data we want to store
some_data = {1: 'one', 2: 'two', 3: 'three'}

#
# create a zipped pickle file
#
zf = zipfile.ZipFile('zipped_pickle.zip', 'w', zipfile.ZIP_DEFLATED)
zf.writestr('data.pkl', cPickle.dumps(some_data))
zf.close()

#
# cPickle.loads works
#
zf = zipfile.ZipFile('zipped_pickle.zip', 'r')
sd1 = cPickle.loads(zf.open('data.pkl').read())
zf.close()

#
# cPickle.load doesn't work
#
zf = zipfile.ZipFile('zipped_pickle.zip', 'r')
sd2 = cPickle.load(zf.open('data.pkl'))
zf.close()

注意:我不只想压缩pickle文件,还有其他类型的文件。这只是一个例子。

你试过使用 import picklefork 吗? :P - FrustratedWithFormsDesigner
3
请告诉我们在这种情况下“不起作用”是什么意思;我们没有预知能力。 - John Machin
@John,复制粘贴他给的代码有多难?在最后一段代码中出现了“EOFError”。 - Alex Martelli
@Alex:一个人复制/粘贴回溯等的工作量远小于多个人将代码复制/粘贴到猜测的Python版本中的工作量。 - John Machin
1
这里没有回溯(因为cPickle是C编码的),而且.open方法是在Python 2.6中引入的——那么,猜猜看呢?(如果OP使用的是2.7——仍然只是一个发布候选版——或者还很少见的Py3,我肯定会期望有提及或标签;-)。并不是为了捍卫发布不完整信息的一般想法,但是这个问题(带有简短、完整、独立的代码)远远超过了SO的平均水平,所以单独批评它似乎对我来说是相当不合适的——当然它可以更好(提及2.6.5或任何特定版本、操作系统使用、EOFError等),但只是些微的改进。 - Alex Martelli
1个回答

8

这是由于zipfile模块中伪文件对象的不完善导致的(用于Python 2.6引入的ZipFile类的.open方法)。考虑以下代码:

>>> f = zf.open('data.pkl')
>>> f.read(1)
'('
>>> f.readline()
'dp1\n'
>>> f.read(1)
''
>>> 
.read(1).readline()的顺序就是.loads内部执行的顺序(在协议0的pickle中,这是Python 2中的默认设置,这也是你在此处使用的)。不幸的是,zipfile存在缺陷,意味着这个特定的顺序不起作用,会在第一次读取/读取行后产生虚假的“文件结束”(.read返回一个空字符串)。我不确定Python标准库中的这个错误是否在Python 2.7中得到了修复--我要检查一下。 编辑:刚刚检查过了--这个错误在Python 2.7 rc1(目前最新的2.7版本)中已经被修复。我还不知道它是否也在最新的2.6 bug-fix版本中得到了修复。 再次编辑:这个错误仍然存在于Python 2.6.5中,这是Python 2.6的最新bug-fix版本--因此,如果您无法升级到2.7并且需要来自ZipFile.open的更好的伪文件对象,则必须回退到2.7修复程序的旧版才是可行的解决方案。
请注意,并不确定您是否需要更好的伪文件对象;如果您控制转储调用并可以使用最新的协议,那么一切都会很好。
>>> zf = zipfile.ZipFile('zipped_pickle.zip', 'w', zipfile.ZIP_DEFLATED)
>>> zf.writestr('data.pkl', cPickle.dumps(some_data, -1))
>>> sd2 = cPickle.load(zf.open('data.pkl'))
>>> 

只有老旧的陈腐、向后兼容的“协议0”(默认值)需要在load中混合读取和读行调用时进行正确的伪文件对象行为(协议0也更慢,导致的Pickle更大,因此除非应用程序必须满足旧Python版本的向后兼容性或0生成的Pickle仅包含ASCII字符,否则不建议使用协议0)。


我在使用Python 2.6.6的协议1和2时遇到了相同的虚假EOFError。 - dvogel

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