2011-03-31 120 views
8

给你的上下文:一次读取多个Python pickle数据,缓冲和换行符?

我有一个大文件f,几个Gig的大小。它包含运行

for obj in objs: cPickle.dump(obj, f)

我想读这个文件时采取缓冲的优势,产生不同的对象的连续咸菜。我想要的是,将几个挑选的对象一次读入缓冲区。这样做的最好方法是什么?我想要一个的模拟腌制数据。事实上,如果挑选的数据确实是换行符分隔符,可以使用readlines,但我不确定这是否属实。

我想到的另一种选择是将dumps() pickled对象先转换为字符串,然后将字符串写入文件,每个文件用换行符分隔。要读取文件,我可以使用readlines()loads()。但我担心一个腌制的物体可能具有"\n"字符,并且会抛弃此文件阅读方案。我的恐惧没有根据吗?

一种选择是通过酸洗它作为一个巨大的物体的名单,但将需要更多的内存,比我能买得起。设置可以通过多线程加速,但我不想在缓冲工作正常之前去那里。什么是这种情况的“最佳做法”。

编辑: 我也可以读取原始字节到一个缓冲区并调用负载,但我需要知道负载消耗了多少个字节的缓冲区,以便我可以把它扔掉。

+1

应该已经在后台发生了缓冲。线程也无济于事。 – 2011-04-01 03:58:41

+0

如果你可以控制pickle文件的创建,你可以使用'pickle.dump(obj,f,pickle.HIGHEST_PROTOCOL)'(或者等价的'pickle.dump(obj,f, -1)'),这是一个二进制协议,比你得到的默认ASCII码更紧凑。拥有更小的文件可能会缓解对缓冲的担忧。事实上,这可能意味着@Kirk Strauser的[答案](http://stackoverflow.com/a/5507750/355230)中的“寻找以''。\ n''”结尾的行将不起作用。 – martineau 2015-01-11 16:26:41

回答

5

file.readlines()返回的文件的全部内容的列表。您需要一次阅读几行。我觉得幼稚代码应该unpickle数据:

import pickle 
infile = open('/tmp/pickle', 'rb') 
buf = [] 
while True: 
    line = infile.readline() 
    if not line: 
     break 
    buf.append(line) 
    if line.endswith('.\n'): 
     print 'Decoding', buf 
     print pickle.loads(''.join(buf)) 
     buf = [] 

如果你有超过生成的泡菜程序中的任何控制,我会选择一个:

  1. 使用shelve模块。
  2. 打印每泡菜的长度(以字节为单位)之前将其写入文件,以便你确切地知道有多少字节每一次的阅读。
  3. 同上,但整数列表写入到一个单独的文件,这样就可以使用这些值的一个索引文件保存咸菜。
  4. 味酸K的对象列表在同一时间。用字节写出这个pickle的长度。写咸菜。重复。

顺便说一句,我怀疑file的内置缓存应该让你的业绩99%的收益,你要寻找的。

如果你确信I/O阻塞了你,你有没有想过尝试mmap()并让操作系统一次处理块?

#!/usr/bin/env python 

import mmap 
import cPickle 

fname = '/tmp/pickle' 
infile = open(fname, 'rb') 
m = mmap.mmap(infile.fileno(), 0, access=mmap.ACCESS_READ) 
start = 0 
while True: 
    end = m.find('.\n', start + 1) + 2 
    if end == 1: 
     break 
    print cPickle.loads(m[start:end]) 
    start = end 
+0

这与我所用的解决方案非常相似,只是我考虑使用readllines,并在读取的字节数上设定了一个预先限定的限制。我保证腌菜内部永远不会有换行符。这是我想知道的关键一点,否则我们是一个错误的解决方案。我有没有正确读到你的个人酱菜以“。\ n”结尾,换句话说,一个点后跟一个换行符?感谢您的其他建议。现在我一直调用cPickle.load()在缓冲的文件对象上,并且cann可以读取大约800个字节的平均每秒200个对象/秒,这仍然很慢。 – san 2011-04-01 00:21:18

+0

我不是专家,但是看着我有数千个泡菜的数据库表,它们都以'。\ n'结尾。看看'pickle.dumps('\ n')';它似乎逃脱'\ n',虽然我不知道这是否有保证。你如何看待#4,你在列表中腌制多个项目,然后从单个文件中解压缩它们.read()? – 2011-04-01 00:38:58

+0

是#4很好。唯一的缺点是消费者将被限制在固定的“缓冲区”。但如果你说“。\ n”是正确的,那么问题已经解决:) – san 2011-04-01 00:48:41

2

你可能想看看shelve模块。它使用数据库模块(如dbm)创建对象的磁盘上字典。对象本身仍然使用pickle序列化。这样你就可以一次读取多组对象而不是一个大酱菜。

+0

感谢您的回复。我看不出这将如何帮助更有效的缓冲。我正在寻找的是一种读取大块字节的方法,并且一次取下一些'K'对象。这个想法是为了缓解I/O瓶颈。我相信你所建议的是我尝试通过提供很多密钥来加载许多对象。模块然后查找与这些键相对应的值。因为我不关心对象的顺序,只是想让它们遍历所有对象,所以不是很多额外的工作,因此是退步。但我可能会误解你。 – san 2011-03-31 23:45:01

2

如果你想缓冲添加到任何文件,通过io.open()打开它。这是一个将从128K块中的底层流中读取的例子。到cPickle.load()每次调用都会从内部缓冲区满足直到它耗尽,那么另一块会从底层的文件阅读:

import cPickle 
import io 

buf = io.open('objects.pkl', 'rb', buffering=(128 * 1024)) 
obj = cPickle.load(buf) 
+0

这正是我所做的,事实上,我读了一个gzip压缩(压缩级别2)文件使用缓冲接口,但我期待尽可能多地抓住尽可能多的腌制对象。所以我得到的反馈是,没有线程,这大多数情况下都是如此之快。 – san 2011-04-01 00:31:39

6

你不需要做任何事情,我想。

>>> import pickle 
>>> import StringIO 
>>> s = StringIO.StringIO(pickle.dumps('apples') + pickle.dumps('bananas')) 
>>> pickle.load(s) 
'apples' 
>>> pickle.load(s) 
'bananas' 
>>> pickle.load(s) 

Traceback (most recent call last): 
    File "<pyshell#25>", line 1, in <module> 
    pickle.load(s) 
    File "C:\Python26\lib\pickle.py", line 1370, in load 
    return Unpickler(file).load() 
    File "C:\Python26\lib\pickle.py", line 858, in load 
    dispatch[key](self) 
    File "C:\Python26\lib\pickle.py", line 880, in load_eof 
    raise EOFError 
EOFError 
>>>