2011-09-19 7 views
12

XMLファイルにいくつかのデータを書きたいと思っています(XMLファイルは約50MBになります)。ルビーの大きなファイルxmlを作成する

ノコギリ(1.5.0)の宝石はであり、解析するには最も効率が良い(ちょうど読み書きされない)ことがわかった。 NokogiriはXMLファイルに書き込むための良い選択肢ではありません。なぜなら、書き込みが完了するまで完全なXMLデータをメモリに保持しているからです。

私はビルダー(3.0.0)が良いオプションであることがわかりましたが、その最良のオプションであるかどうかはわかりません。

私は、次の簡単なコードで、いくつかのベンチマークを試してみました:

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

鋸山を約143秒かかり、また、メモリの消費量が徐々に増加し、約700メガバイトでようやく終わりました。

ビルダーの所要時間は約123秒で、メモリ消費量は10 MBで十分でした。

Rubyに巨大なXMLファイル(50 MB)を書き込むためのよりよい解決策がありますか?

鋸山ファイル:

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 
+0

再解析:鋸山はかなりユーザーフレンドリーですが、スピードが鍵となるとき、私は(同様nogokiriで利用可能)SAXパーサを書くために行きます。私は便利なユーティリティクラスを用意しています。これは、xmlから必要なものの配列をすばやく構築するために使用します(xmlはかなりシンプルです)。もしそうでなければ、https://gist.github.com/854726カスタムsaxparserを作成します。 – sunkencity

+0

あなたはそれを他の方法で取った。配列(アクティブレコード)からxmlを構築したい。 –

+0

「私はnokogiri(1.5.0)の宝石を解析するのが最も効率的だ」とコメントしました。私の主張は、saxparser apiを直接使うことです。 – sunkencity

答えて

15

ソリューション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) 

です:インデント=>この場合には大きな違いをしない真が、それはやるかもしれない、より複雑なXMLファイルの。

(インデント付き)$時間ルビー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 
+0

に感謝しますが、このメモリは600メガバイトになります。それはあまりにも間違っているのですか? –

+0

私はメモリを食べることなくそれを行う方法を追加しました。速いですが、自動インデントのようなxmlジェネレータや妥当性チェックなどの利点はありません。ソリューション2の場合は – sunkencity

+0

、なぜビルダー自体を使用しないのですか?それは検証を提供し、より安全になるでしょうか? –

関連する問題