2011-09-19 13 views
12

Chcę zapisać niektóre dane do pliku XML (plik XML powinien osiągnąć ~ 50 MB).tworzenie dużego pliku xml w rubinach

Znalazłem klejnot nokogiri (1.5.0), aby być najbardziej wydajny do parsowania (tylko czytać i nie pisać). Nokogiri nie jest dobrą opcją zapisu do pliku XML, ponieważ przechowuje kompletne dane XML w pamięci, dopóki nie zapisze się w zapisie.

Znalazłem builder (3.0.0) jako dobrą opcję, ale nie jestem pewien, czy to najlepsza opcja

próbowałem jakiś wzorzec z poniższego prostego kodu:

(1..500000).each do |k| 
    xml.products { 
     xml.widget { 
     xml.id_ k 
     xml.name "Awesome widget" 
     } 
    } 
    end 

Nokogiri zajmuje około 143 sekund, a także zużycie pamięci stopniowo zwiększana, a zakończył się ostatecznie około 700 MB.

Builder trwał około 123 sekundy, a zużycie pamięci było wystarczająco stabilne na poziomie 10 MB.

Czy istnieje lepsze rozwiązanie do pisania dużych plików XML (50 MB) w języku Ruby? Plik

Nokogiri: file

require 'rubygems' 
require 'nokogiri' 
a = Time.now 
builder = Nokogiri::XML::Builder.new do |xml| 
    xml.root { 
    (1..500000).each do |k| 
    xml.products { 
     xml.widget { 
     xml.id_ k 
     xml.name "Awesome widget" 
     } 
    } 
    end 
    } 
end 
o = File.new("test_noko.xml", "w") 
o.write(builder.to_xml) 
o.close 
puts (Time.now-a).to_s 

Builder:

require 'rubygems' 
require 'builder' 
a = Time.now 
File.open("test.xml", 'w') {|f| 
xml = Builder::XmlMarkup.new(:target => f, :indent => 1) 

    (1..500000).each do |k| 
    xml.products { 
     xml.widget { 
     xml.id_ k 
     xml.name "Awesome widget" 
     } 
    } 
    end 

} 
puts (Time.now-a).to_s 
+0

Re analizowaniem: Nokogiri jest przyjazny dość łatwy, ale gdy prędkość jest kluczem, idę za jedyne napisanie parsera sax (dostępny w nogokiri również) . Mam poręczną klasę narzędziową, której używam do niesamowicie szybkiego budowania tablicy rzeczy, których potrzebuję z xml (pod warunkiem, że xml jest całkiem prosty) https://gist.github.com/854726 Jeśli jeszcze nie, będę musiał napisz niestandardowy saxparser. – sunkencity

+0

Zrobiłeś to inaczej. Chcę zbudować xml z tablicy (aktywny rekord). –

+0

To był komentarz do "Znalazłem klejnot nokogiri (1.5.0), aby być najbardziej efektywnym do analizowania", moim celem jest najbardziej efektywnym sposobem analizowania, jest użycie apletu saxparser bezpośrednio. – sunkencity

Odpowiedz

15

Rozwiązanie 1

Jeśli prędkość jest głównym problemem, bym po prostu używać libxml-ruby (http://libxml.rubyforge.org/rdoc/) bezpośrednio:

$ time ruby test.rb 

real 0m7.352s 
user 0m5.867s 
sys  0m0.921s 

API jest dość prosta

require 'rubygems' 
require 'xml' 
doc = XML::Document.new() 
doc.root = XML::Node.new('root_node') 
root = doc.root 

500000.times do |k| 
    root << elem1 = XML::Node.new('products') 
    elem1 << elem2 = XML::Node.new('widget') 
    elem2['id'] = k.to_s 
    elem2['name'] = 'Awesome widget' 
end 

doc.save('foo.xml', :indent => false, :encoding => XML::Encoding::UTF_8) 

używając: tiret => true nie robi wielkiej różnicy w tym przypadku, ale w przypadku bardziej skomplikowanych plików xml może to zrobić.

$ czas rubin test.rb # (z tiret)

real 0m7.395s 
user 0m6.050s 
sys  0m0.847s 

Rozwiązanie 2

Oczywiście najszybszym rozwiązaniem, i że nie buduje się na pamięć jest po prostu napisać xml ręcznie, ale to z łatwością wygeneruje inne źródła błędów, jak prawdopodobnie nieprawidłowe xml.

$ time ruby test.rb 

real 0m1.131s 
user 0m0.873s 
sys  0m0.126s 

oto kod, który:

f = File.open("foo.xml", "w") 
f.puts('<doc>') 
500000.times do |k| 
    f.puts "<product><widget id=\"#{k}\" name=\"Awesome widget\" /></product>" 
end 
f.puts('</doc>') 
f.close 
+0

ale z tą pamięcią idzie do 600 MB .. to jest o wiele za złe prawda? –

+0

Dodałem sposób, aby to zrobić bez spożywania pamięci, jest to szybsze, ale nie masz żadnych korzyści z używania generatora xml, takiego jak automatyczne wcięcie, i sprawdzania poprawności itp. – sunkencity

+0

w przypadku rozwiązania 2, dlaczego nie skorzystać z samego Buildera? zapewniłoby to walidację, a także byłoby bezpieczniejsze, prawda? –