我有一个非常大的XML文件(300mb),格式如下:
<data>
<point>
<id><![CDATA[1371308]]></id>
<time><![CDATA[15:36]]></time>
</point>
<point>
<id><![CDATA[1371308]]></id>
<time><![CDATA[15:36]]></time>
</point>
<point>
<id><![CDATA[1371308]]></id>
<time><![CDATA[15:36]]></time>
</point>
</data>
现在我需要阅读它并遍历point
节点为每个节点做一些事情。目前我正在和Nokogiri这样做:
require 'nokogiri'
xmlfeed = Nokogiri::XML(open("large_file.xml"))
xmlfeed.xpath("./data/point").each do |item|
save_id(item.xpath("./id").text)
end
然而,这并不是非常有效,因为它解析了整个拥抱的一切,因此创造了巨大的内存占用(几GB)。
有没有办法在块中执行此操作?如果我没有弄错的话,可能会被称为流媒体?
修改
使用nokogiris sax解析器的建议答案可能没问题,但是当每个point
中有多个节点需要从中提取内容并以不同方式处理时,它会变得非常混乱。我宁愿选择一次访问一个point
,然后处理它,然后继续下一个&#34;忘记&#34;而不是返回大量的条目供以后处理。以前的。
答案 0 :(得分:6)
鉴于这个鲜为人知(但很棒)gist使用Nokogiri的Reader界面,你应该能够做到这一点:
Xml::Parser.new(Nokogiri::XML::Reader(open(file))) do
inside_element 'point' do
for_element 'id' do puts "ID: #{inner_xml}" end
for_element 'time' do puts "Time: #{inner_xml}" end
end
end
有人应该把它变成宝石,也许是我;)
答案 1 :(得分:2)
使用Nokogiri::XML::SAX::Parser
(事件驱动的解析器)和Nokogiri::XML::SAX::Document
:
require 'nokogiri'
class IDCollector < Nokogiri::XML::SAX::Document
attr :ids
def initialize
@ids = []
@inside_id = false
end
def start_element(name, attrs)
# NOTE: This is simplified. You need some kind of stack manipulations
# (push in start_element / pop in end_element)
# to correctly pick `.//data/point/id` elements.
@inside_id = true if name == 'id'
end
def end_element(name)
@inside_id = false
end
def cdata_block string
@ids << string if @inside_id
end
end
collector = IDCollector.new
parser = Nokogiri::XML::SAX::Parser.new(collector)
parser.parse(File.open('large_file.xml'))
p collector.ids # => ["1371308", "1371308", "1371308"]
Nokogiri::XML::SAX::Parser
:是一个读取它的SAX样式解析器 输入,因为它认为必要。
如果您需要更多控制文件输入,也可以使用Nokogiri::XML::SAX::PushParser
。
答案 2 :(得分:0)
如果你使用jruby,你可以利用vtd-xml,它具有最高效的内存模型,比DOM效率高3~5倍。