Sembra che tu sia un po 'sottaceto! ;-). Spero che dopo questo, non userete MAI MAI IL MAIALE. Non è un ottimo formato di archiviazione dei dati.
In ogni caso, per questa risposta presumo che la tua classe Document
assomigli un po 'a questo.In caso contrario, lasciare un commento con il vostro attuale classe Document
:
class Document(object): # <-- object part is very important! If it's not there, the format is different!
def __init__(self, title, date, text): # assuming all strings
self.title = title
self.date = date
self.text = text
In ogni modo, ho fatto alcuni dati di test semplici con questa classe:
d = [Document(title='foo', text='foo is good', date='1/1/1'), Document(title='bar', text='bar is better', date='2/2/2'), Document(title='baz', text='no one likes baz :(', date='3/3/3')]
in salamoia con formato 2
(pickle.HIGHEST_PROTOCOL
per Python 2.x)
>>> s = pickle.dumps(d, 2)
>>> s
'\x80\x02]q\x00(c__main__\nDocument\nq\x01)\x81q\x02}q\x03(U\x04dateq\x04U\x051/1/1q\x05U\x04textq\x06U\x0bfoo is goodq\x07U\x05titleq\x08U\x03fooq\tubh\x01)\x81q\n}q\x0b(h\x04U\x052/2/2q\x0ch\x06U\rbar is betterq\rh\x08U\x03barq\x0eubh\x01)\x81q\x0f}q\x10(h\x04U\x053/3/3q\x11h\x06U\x13no one likes baz :(q\x12h\x08U\x03bazq\x13ube.'
e smontato con pickletools
:
>>> pickletools.dis(s)
0: \x80 PROTO 2
2: ] EMPTY_LIST
3: q BINPUT 0
5: ( MARK
6: c GLOBAL '__main__ Document'
25: q BINPUT 1
27:) EMPTY_TUPLE
28: \x81 NEWOBJ
29: q BINPUT 2
31: } EMPTY_DICT
32: q BINPUT 3
34: ( MARK
35: U SHORT_BINSTRING 'date'
41: q BINPUT 4
43: U SHORT_BINSTRING '1/1/1'
50: q BINPUT 5
52: U SHORT_BINSTRING 'text'
58: q BINPUT 6
60: U SHORT_BINSTRING 'foo is good'
73: q BINPUT 7
75: U SHORT_BINSTRING 'title'
82: q BINPUT 8
84: U SHORT_BINSTRING 'foo'
89: q BINPUT 9
91: u SETITEMS (MARK at 34)
92: b BUILD
93: h BINGET 1
95:) EMPTY_TUPLE
96: \x81 NEWOBJ
97: q BINPUT 10
99: } EMPTY_DICT
100: q BINPUT 11
102: ( MARK
103: h BINGET 4
105: U SHORT_BINSTRING '2/2/2'
112: q BINPUT 12
114: h BINGET 6
116: U SHORT_BINSTRING 'bar is better'
131: q BINPUT 13
133: h BINGET 8
135: U SHORT_BINSTRING 'bar'
140: q BINPUT 14
142: u SETITEMS (MARK at 102)
143: b BUILD
144: h BINGET 1
146:) EMPTY_TUPLE
147: \x81 NEWOBJ
148: q BINPUT 15
150: } EMPTY_DICT
151: q BINPUT 16
153: ( MARK
154: h BINGET 4
156: U SHORT_BINSTRING '3/3/3'
163: q BINPUT 17
165: h BINGET 6
167: U SHORT_BINSTRING 'no one likes baz :('
188: q BINPUT 18
190: h BINGET 8
192: U SHORT_BINSTRING 'baz'
197: q BINPUT 19
199: u SETITEMS (MARK at 153)
200: b BUILD
201: e APPENDS (MARK at 5)
202: . STOP
Sembra complesso! Ma davvero, non è così male. pickle
è fondamentalmente una macchina stack, ogni identificatore ALL_CAPS che vedi è un codice operativo, che manipola lo "stack" interno in qualche modo per la decodifica. Se stessimo cercando di analizzare una struttura complessa, questo sarebbe più importante, ma per fortuna stiamo solo facendo una semplice lista di essenzialmente-tuple. Tutto questo "codice" sta facendo costruire un mucchio di oggetti nello stack e poi spingere l'intero stack in un elenco.
L'unica cosa di cui dobbiamo preoccuparci sono i codici "BINPUT"/"BINGET" che vedete sparsi in giro. Fondamentalmente, questi sono per 'memoization', per ridurre l'impronta dei dati, pickle
salva stringhe con BINPUT <id>
, e poi se vengono di nuovo, invece di ri-scaricarli, semplicemente mette uno BINGET <id>
per recuperarli dalla cache.
Inoltre, un'altra complicazione! C'è oltre il SHORT_BINSTRING
- c'è il normale BINSTRING
per stringhe> 256 byte e anche alcune divertenti varianti Unicode. Immagino che tu stia usando Python 2 con tutte le stringhe ASCII. Di nuovo, commenta se questo non è un presupposto corretto.
OK, quindi è necessario eseguire lo streaming del file fino a quando non si preme un byte '\ 81' (NEWOBJ
). Quindi, dobbiamo eseguire la scansione in avanti fino a quando non si preme un carattere '(' (MARK
). Quindi, finché non si preme su 'u' (SETITEMS
), leggiamo coppie di stringhe chiave/valore: ci dovrebbero essere 3 coppie in totale, una per ogni campo
Quindi, facciamo questo: ecco la mia sceneggiatura per leggere i dati di pickle in streaming. È tutt'altro che perfetto, dal momento che ho appena hackerato insieme per questa risposta, e avrete bisogno di modificarlo molto, ma è un buon inizio
pickledata = '\x80\x02]q\x00(c__main__\nDocument\nq\x01)\x81q\x02}q\x03(U\x04dateq\x04U\x051/1/1q\x05U\x04textq\x06U\x0bfoo is goodq\x07U\x05titleq\x08U\x03fooq\tubh\x01)\x81q\n}q\x0b(h\x04U\x052/2/2q\x0ch\x06T\x14\x05\x00\x00bar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterq\rh\x08U\x03barq\x0eubh\x01)\x81q\x0f}q\x10(h\x04U\x053/3/3q\x11h\x06U\x13no one likes baz :(q\x12h\x08U\x03bazq\x13ube.'
# simulate a file here
import StringIO
picklefile = StringIO.StringIO(pickledata)
import pickle # just for opcode names
import struct # binary unpacking
def try_memo(f, v, cache):
opcode = f.read(1)
if opcode == pickle.BINPUT:
cache[f.read(1)] = v
elif opcode == pickle.LONG_BINPUT:
print 'skipping LONG_BINPUT to save memory, LONG_BINGET will probably not be used'
f.read(4)
else:
f.seek(f.tell() - 1) # rewind
def try_read_string(f, opcode, cache):
if opcode in [ pickle.SHORT_BINSTRING, pickle.BINSTRING ]:
length_type = 'b' if opcode == pickle.SHORT_BINSTRING else 'i'
str_length = struct.unpack(length_type, f.read(struct.calcsize(length_type)))[0]
value = f.read(str_length)
try_memo(f, value, memo_cache)
return value
elif opcode == pickle.BINGET:
return memo_cache[f.read(1)]
elif opcide == pickle.LONG_BINGET:
raise Exception('Unexpected LONG_BINGET? Key ' + f.read(4))
else:
raise Exception('Invalid opcode ' + opcode + ' at pos ' + str(f.tell()))
memo_cache = {}
while True:
c = picklefile.read(1)
if c == pickle.NEWOBJ:
while picklefile.read(1) != pickle.MARK:
pass # scan forward to field instantiation
fields = {}
while True:
opcode = picklefile.read(1)
if opcode == pickle.SETITEMS:
break
key = try_read_string(picklefile, opcode, memo_cache)
value = try_read_string(picklefile, picklefile.read(1), memo_cache)
fields[key] = value
print 'Document', fields
# insert to sqllite
elif c == pickle.STOP:
break
Questa legge correttamente i miei dati di test in formato salamoia 2 (modificato per avere una lunga serie):.
$ python picklereader.py
Document {'date': '1/1/1', 'text': 'foo is good', 'title': 'foo'}
Document {'date': '2/2/2', 'text': 'bar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is better', 'title': 'bar'}
Document {'date': '3/3/3', 'text': 'no one likes baz :(', 'title': 'baz'}
Buona fortuna!
Quanta RAM ha la macchina che esegue il programma? – IanAuld
Usa sqlite, per decapare un file da 2.6 Gb è abbastanza vicino alla pazzia. Sqlite è abbastanza facile da aggirare;) – brunsgaard
Condividi i dati e me ne interromperò la distribuzione o noleggi una grande macchina AWS per il lavoro. Sono abbastanza sicuro che @IanAuld abbia ragione ... ti manca la memoria. – brunsgaard