假设我使用 pickle.dump 来存储一个整数值,并且我想通过加 1 来更新该整数。以下代码有效:
with open('../CONFIG.txt', 'rb') as ofile:
value = pickle.load(ofile)
with open('../CONFIG.txt', 'wb') as ofile:
pickle.dump(value + 1, ofile, protocol=2)
但是一旦我使用 r+b 模式,它就不会:
with open('../CONFIG.txt', 'r+b') as ofile:
value = pickle.load(ofile)
pickle.dump(value + 1, ofile, protocol=2)
为什么第二个代码不起作用?有没有办法在不打开文件两次的情况下更新文件内容?
最佳答案
更新 2:
如果您确实需要使用同一个文件读取和写入 pickle 数据,最好单独执行,一次使用 'r'
,然后使用 'w'
模式。 “打开文件”的行为并不像读取或写入文件那么昂贵。打开文件,也称为获取文件句柄,速度相当快。
如果您关心 'r+b'
模式下读取/写入的数据大小,则 RAM 中的大小比读取/写入更重要。您不会跳过时间、CPU 周期或磁盘操作的该步骤。
还有可读性。考虑使用 'rb'
和 'wb'
的原始版本(4 行,易于理解)与“在一个 block 中完成”方法中的 6 行。显式的 seek(0)
和 truncate()
无论如何都会在单独的 'wb'
block 中发生。可能也会影响性能。单 block 方法没有任何好处,读取起来更差,写入时容易出错 - 需要 3 次编辑才能解决单 block 方法产生的问题。 :-/
原始答案:
正如 @glibdud 在评论中提到的,执行 pickle.load()
后,文件指针将移至末尾。
如果您想使用'r+b'
模式将数据写回文件,则使用ofile.seek()
修改光标的位置。在这种情况下,输入 ofile.seek(0)
将光标移回文件开头。
>>> with open('CONFIG.txt', 'r+b') as ofile:
... value = pickle.load(ofile)
... print value
... # re-position the cursor to the start of the file before dumping new data
... ofile.seek(0)
... pickle.dump(value + 1, ofile, protocol=2)
... # truncate anything left in the file if the prev pickled data was larger
... ofile.truncate()
...
3
>>>
>>> # let's read again to see
...
>>> with open('CONFIG.txt', 'r+b') as ofile:
... value = pickle.load(ofile)
... print value
...
4
>>>
更新 1 和 3:
Wrt @user2357112 关于截断文件的评论:我认为没有必要。我用pickle转储了一个较长的文本,然后让它只写一个int,它似乎已经正确覆盖了。也许转储文件需要更大才能验证是否存在问题;或者协议(protocol) 0 的 ascii 格式容易受到它的影响,而协议(protocol) >= 1 的二进制格式则更容易受到影响。 更正: 如注释中所述,额外的数据仍在文件中。所以file.truncate()
是需要的。请参阅下面的评论。幸运的是,在默认用法(无参数)中,它会截断到当前位置。
关于python - Python 的 pickle 是否以 r+b 模式工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44595113/