2013-10-10 2 views
6

По сути, у меня есть XML-файл объемом 6,4 Гбайт, который я бы хотел преобразовать в JSON, а затем сохранить на диск. В настоящее время я запускаю OSX 10.8.4 с i7 2700k и 16GB RAM и запускаю Python 64bit (дважды проверяется). Я получаю сообщение об ошибке, что у меня недостаточно памяти для выделения. Как я могу это исправить?Python - конвертировать очень большие (6.4GB) XML-файлы в JSON

print 'Opening' 
f = open('large.xml', 'r') 
data = f.read() 
f.close() 

print 'Converting' 
newJSON = xmltodict.parse(data) 

print 'Json Dumping' 
newJSON = json.dumps(newJSON) 

print 'Saving' 
f = open('newjson.json', 'w') 
f.write(newJSON) 
f.close() 

Ошибка:

Python(2461) malloc: *** mmap(size=140402048315392) failed (error code=12) 
*** error: can't allocate region 
*** set a breakpoint in malloc_error_break to debug 
Traceback (most recent call last): 
    File "/Users/user/Git/Resources/largexml2json.py", line 10, in <module> 
    data = f.read() 
MemoryError 
+0

Try 'readlines' вместо' read'. Метод 'read' возвращает строку, а строка занимает * непрерывное * пространство в памяти, и обычно нет большого (> 100 МБ) непрерывного пространства в доступной памяти. 'readlines' предоставит вам список строк и работает относительно хорошо для больших данных. – Igonato

+0

python имеет тенденцию иметь несколько большие издержки памяти для таких вещей ... например: 'from lxml import etree; e = etree.Element ('x'); e .__ sizeof __() 'возвращает 0x30 - 48 байтов для более или менее пустого элемента. 'd = dict(); d .__ sizeof __() 'возвращает 0xf8. то вы читаете xml и затем воссоздаете его как dict, в основном удваивая его использование памяти. вам нужно будет найти инкрементный метод. –

+1

Одна загадка здесь, почему 'mmap' сообщает, что запрашиваемый размер составляет 140 402 048 315 392. Я, это большой файл, но даже не Python ;-) должен подумать, что для этого ему нужно 140 триллионов байт. –

ответ

8

Многие библиотеки Python XML поддерживают разборе XML субэлементов пошагово, например, xml.etree.ElementTree.iterparse и xml.sax.parse в стандартной библиотеке. Эти функции обычно называются «XML Stream Parser».

Библиотека xmltodict, которую вы использовали, также имеет режим потоковой передачи. Я думаю, что это может решить вашу проблему

https://github.com/martinblech/xmltodict#streaming-mode

2

Вместо того, чтобы пытаться прочитать файл на одном дыхании, а затем обработать его, вы хотите, чтобы прочитать его на куски и обрабатывать каждый кусок, как он загружен. Это довольно распространенная ситуация при обработке больших XML-файлов и покрывается стандартом Simple API for XML (SAX), который указывает API обратного вызова для анализа XML-потоков - он является частью стандартной библиотеки Python под xml.sax.parse и xml.etree.ETree, как указано выше.

Вот быстрый XML в JSON конвертер:

from collections import defaultdict 
import json 
import sys 
import xml.etree.ElementTree as ET 

def parse_xml(file_name): 
    events = ("start", "end") 
    context = ET.iterparse(file_name, events=events) 

    return pt(context) 

def pt(context, cur_elem=None): 
    items = defaultdict(list) 

    if cur_elem: 
     items.update(cur_elem.attrib) 

    text = "" 

    for action, elem in context: 
     # print("{0:>6} : {1:20} {2:20} '{3}'".format(action, elem.tag, elem.attrib, str(elem.text).strip())) 

     if action == "start": 
      items[elem.tag].append(pt(context, elem)) 
     elif action == "end": 
      text = elem.text.strip() if elem.text else "" 
      break 

    if len(items) == 0: 
     return text 

    return { k: v[0] if len(v) == 1 else v for k, v in items.items() } 

if __name__ == "__main__": 
    json_data = parse_xml("large.xml") 
    print(json.dumps(json_data, indent=2)) 

Если вы смотрите на много обработки XML проверить lxml библиотеки, он получил массу полезных вещей сверх стандартных модулей, а также намного проще в использовании.

http://lxml.de/tutorial.html

+1

Для тех, кто заканчивает этот ответ, не будьте идиотом, как я, и используйте значение elem.text в 'action == 'start''. Может показаться, что это работает, но парсер не гарантирует, что вы получите текст. Очень редко вы получаете значения None и вытаскиваете свои волосы за причины. – MarkM

+1

Также, когда этот ответ стал отличным началом для изучения ElementTree, его использование памяти может быть дополнительно уменьшено добавлением 'elem.clear()' непосредственно перед 'break'. – MarkM

Смежные вопросы