我想把一些数据写入
XML文件(
XML文件会到〜50 MB).
我发现nokogiri(1.5.0)宝石是最有效的解析(只读和不写). Nokogiri不是写入XML文件的好选择,因为它在内存中保存完整的XML数据,直到写入最后写入它.
我发现builder(3.0.0)是一个很好的选择,但不知道它是否是最佳选择.
我使用以下简单代码尝试了一些基准测试:
(1..500000).each do |k| xml.products { xml.widget { xml.id_ k xml.name "Awesome widget" } } end
Nokogiri需要大约143秒钟,内存消耗逐渐增加,最终达到约700 MB.
Builder耗时约123秒,内存消耗稳定在10 MB.
那么在Ruby中编写巨大的XML文件(50 MB)有更好的解决方案?
Nokogiri文件:
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
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
解决方法
解决方案1
如果速度是您的主要关注点,我只需直接使用libxml-ruby(http://libxml.rubyforge.org/rdoc/):
$time ruby test.rb real 0m7.352s user 0m5.867s sys 0m0.921s
api很直接
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)
使用:indent =>在这种情况下,true并没有太多的区别,但是对于更复杂的xml文件,它可能会做.
$time ruby test.rb#(带缩进)
real 0m7.395s user 0m6.050s sys 0m0.847s
解决方案2
当然最快的解决方案,而不是建立在内存上只是为了手动编写xml,但是很容易产生错误的其他来源,例如可能是无效的xml.
$time ruby test.rb real 0m1.131s user 0m0.873s sys 0m0.126s
这里是代码:
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