2013-10-07 4 views
6

Sto tentando di decodificare un grande file utf-8 json (2,2 GB). I caricare il file in questo modo:2,2 GB Il file JSON analizza in modo incoerente

f = codecs.open('output.json', encoding='utf-8') 
data = f.read() 

Se provo a fare qualsiasi di: json.load, json.loads o json.JSONDecoder().raw_decode ottengo l'errore:

--------------------------------------------------------------------------- 
ValueError        Traceback (most recent call last) 
<ipython-input-40-fc2255017b19> in <module>() 
----> 1 j = jd.decode(data) 

/usr/lib/python2.7/json/decoder.pyc in decode(self, s, _w) 
    367   end = _w(s, end).end() 
    368   if end != len(s): 
--> 369    raise ValueError(errmsg("Extra data", s, end, len(s))) 
    370   return obj 
    371 

ValueError: Extra data: line 1 column -2065998994 - line 1 column 2228968302 
    (char -2065998994 - 2228968302) 


uname -m mostra x86_64 e

> python -c 'import sys;print("%x" % sys.maxsize, sys.maxsize > 2**32)' 
('7fffffffffffffff', True)` 

quindi dovrei essere su 64 bit e la dimensione dei numeri interi non dovrebbe essere un problema.

Tuttavia, se corro:

jd = json.JSONDecoder() 
len(data) # 2228968302 
j = jd.raw_decode(data) 
j[1] # 2228968302 

Il secondo valore nella tupla restituita da raw_decode è la fine della stringa, in modo raw_decode sembra per analizzare l'intero file con apparentemente senza spazzatura al fine.

Quindi, c'è qualcosa che dovrei fare diversamente con il json? Lo raw_decode decodifica effettivamente l'intero file? Perché è json.load(s) in errore?

+2

Che tipo di sistema stai utilizzando? 2.2 miliardi sono troppo grandi per un numero intero a 32 bit con segno e i numeri negativi nei dettagli delle eccezioni suggeriscono che si stanno verificando dei problemi. –

+0

Senza guardare il codice sottostante, ho intenzione di indovinare che le funzioni convertono l'input in una stringa e che hanno problemi di overflow che cercano di gestire una stringa di tale dimensione. La versione "raw" presumibilmente non lo è, e quindi è in grado di analizzare il tutto. – Gabe

+0

@TimPeters Ho aggiunto questo alla mia domanda ma sono su un'architettura a 64 bit. –

risposta

9

Vorrei aggiungere questo come commento, ma le funzionalità di formattazione nei commenti sono troppo limitate.

Fissando il codice sorgente,

raise ValueError(errmsg("Extra data", s, end, len(s))) 

chiama questa funzione:

def errmsg(msg, doc, pos, end=None): 
    ... 
    fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})' 
    return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end) 

La (char {5} - {6}) parte del formato è questa parte del messaggio di errore che avete mostrato:

(char -2065998994 - 2228968302) 

Quindi, in errmsg(), pos è -2065998994 e end è 2228968302. Ecco! ;-):

>>> pos = -2065998994 
>>> end = 2228968302 
>>> 2**32 + pos 
2228968302L 
>>> 2**32 + pos == end 
True 

Cioè, pos e end sono "davvero" lo stesso. Tornato da dove è stato chiamato il numero errmsg(), che significa end e len(s) sono davvero uguali, ma lo end viene visualizzato come numero intero con segno a 32 bit. end a sua volta deriva da un metodo end() dell'oggetto con corrispondenza regolare dell'espressione.

Quindi il vero problema qui sembra essere una limitazione/assunzione a 32 bit nel motore regexp. Ti incoraggio a open a bug report!

Successivamente: per rispondere alle vostre domande, sì, raw_decode() sta decodificando l'intero file. Gli altri metodi chiamanoraw_decode(), ma successivamente aggiungono i (inefficienti!) Controlli di integrità.