2011-09-19 12 views
12

Quiero escribir algunos datos en un archivo XML (el archivo XML vendría a ~ 50 MB).creando archivos grandes xml en ruby ​​

Encontré nokogiri (1.5.0) joya para ser el más eficiente para analizar (solo leer y no escribir). Nokogiri no es una buena opción para escribir en un archivo XML, ya que contiene los datos XML completos en la memoria hasta que finalmente los escribe.

Encontré el constructor (3.0.0) como una buena opción pero no estoy seguro si es la mejor opción.

he intentado algún punto de referencia con el siguiente código simple:

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

Nokogiri toma alrededor de 143 segundos y también el consumo de memoria aumenta gradualmente y finalmente terminó en alrededor de 700 MB.

El generador tardó unos 123 segundos y el consumo de memoria fue lo suficientemente estable en 10 MB.

¿Existe una mejor solución para escribir enormes archivos XML (50 MB) en Ruby? archivo

Nokogiri: archivo

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 

Constructor:

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 de análisis: Nokogiri es bastante fácil de usar, pero cuando la velocidad es la clave, voy por sólo escribir un analizador SAX (disponible en nogokiri también) . Tengo una práctica clase de utilidades que utilizo para crear rápidamente una variedad de cosas que necesito de un xml (siempre que el xml sea bastante simple) https://gist.github.com/854726 de lo contrario podría tener que escribe un saxparser personalizado. – sunkencity

+0

Lo tomó de otra manera ... Quiero construir xml desde una matriz (registro activo). –

+0

Era un comentario sobre "Encontré nokogiri (1.5.0) joya para ser el más eficiente para analizar", mi punto es que la forma más eficiente de analizar es usar la api saxparser directamente. – sunkencity

Respuesta

15

Solución 1

Si la velocidad es su principal preocupación, que acababa de utilizar libxml-rubí (http://libxml.rubyforge.org/rdoc/) directamente:

$ time ruby test.rb 

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

El API es bastante sencillo

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) 

usando: guión => cierto no hace mucha diferencia en este caso, pero para archivos XML más complejos que podría hacer.

tiempo $ rubí test.rb # (con guión)

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

Solución 2

Por supuesto, la solución más rápida, y que no se acumula en la memoria es sólo para escribir la XML manualmente, pero eso generará fácilmente otras fuentes de error, como posiblemente no válido xml.

$ time ruby test.rb 

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

aquí está el código para que:

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

pero con esta memoria va hasta 600 mb .. eso está demasiado mal ¿no? –

+0

Agregué una forma de hacerlo sin consumir la memoria, es más rápido, pero no obtiene ninguno de los beneficios de usar un generador xml como la sangría automática, y las verificaciones de validez, etc. – sunkencity

+0

en el caso de la solución 2, ¿Por qué no usar el constructor en sí? , proporcionaría validación y también sería más seguro, ¿no? –