2011-09-19 14 views
12

Desidero scrivere alcuni dati in un file XML (il file XML verrebbe a ~ 50 MB).creazione di file xml di grandi dimensioni in rubino

Ho trovato nokogiri (1.5.0) gem per essere il più efficiente da analizzare (Basta leggere e non scrivere). Nokogiri non è una buona opzione per scrivere in un file XML poiché conserva i dati XML completi in memoria fino a quando non scrive definitivamente.

Ho trovato il builder (3.0.0) per essere una buona opzione, ma non sono sicuro che sia l'opzione migliore.

Ho provato alcuni benchmark con il seguente codice semplice:

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

Nokogiri dura circa 143 secondi e anche il consumo di memoria aumentata gradualmente e si è conclusa, infine, a circa 700 MB.

Il builder ha impiegato circa 123 secondi e il consumo di memoria era abbastanza stabile a 10 MB.

Quindi c'è una soluzione migliore per scrivere enormi file XML (50 MB) in Ruby? file di

Nokogiri: file di

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 di analisi: Nokogiri è abbastanza facile da usare, ma quando la velocità è la chiave, vado solo per la scrittura di un parser SAX (disponibile in nogokiri pure) . Ho una pratica classe di utilità che uso per creare rapidamente una serie di elementi di cui ho bisogno da un xml (a condizione che l'xml sia piuttosto semplice) https://gist.github.com/854726 se potrei dover scrivi un saxparser personalizzato. – sunkencity

+0

Lo hai preso in un altro modo .. Voglio costruire xml da array (record attivo). –

+0

E 'stato un commento su "Ho trovato gemma nokogiri (1.5.0) per essere il più efficiente da analizzare", il mio punto è il modo più efficace per analizzare è quello di utilizzare direttamente l'API saxparser. – sunkencity

risposta

15

Soluzione 1

Se la velocità è la vostra preoccupazione principale, mi basta usare libxml-ruby (http://libxml.rubyforge.org/rdoc/) direttamente:

$ time ruby test.rb 

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

L'API è piuttosto semplice

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) 

utilizzando: trattino => true non fa molta differenza, in questo caso, ma per i file XML più complessi potrebbe fare.

$ tempo rubino test.rb # (con trattino)

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

Soluzione 2

Naturalmente la soluzione più veloce, e che non si accumula sulla memoria è solo scrivere il xml manualmente ma che genererà facilmente altre fonti di errore come xml eventualmente non valido.

$ time ruby test.rb 

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

Ecco il codice per questo:

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

ma con questa memoria arriva fino a 600 mb .. che è troppo sbagliato, vero? –

+0

Ho aggiunto un modo per farlo senza consumare la memoria, è più veloce, ma non ottieni nessuno dei vantaggi dell'uso di un generatore xml come il rientro automatico, i controlli di validità ecc. – sunkencity

+0

nel caso della soluzione 2, perché non usare il costruttore stesso? , fornirebbe la convalida e sarebbe anche più sicuro, non è vero? –

Problemi correlati