使用libkml-ruby chunk处理大型XML文件

时间:2010-01-04 15:12:51

标签: ruby stream libxml-ruby

我想在Ruby中使用libxml读取包含超过一百万个小书目记录(如<article>...</article>)的大XML文件。我已经尝试将Reader类与expand方法结合使用来按记录读取记录,但我不确定这是正确的方法,因为我的代码占用了内存。因此,我正在寻找一个如何方便地处理记录的配方,并且内存使用量不断增加。下面是我的主循环:

   File.open('dblp.xml') do |io|
      dblp = XML::Reader.io(io, :options => XML::Reader::SUBST_ENTITIES)
      pubFactory = PubFactory.new

      i = 0
      while dblp.read do
        case dblp.name
          when 'article', 'inproceedings', 'book': 
            pub = pubFactory.create(dblp.expand)
            i += 1
            puts pub
            pub = nil
            $stderr.puts i if i % 10000 == 0
            dblp.next
          when 'proceedings','incollection', 'phdthesis', 'mastersthesis':
            # ignore for now
            dblp.next 
          else
            # nothing
        end
      end  
    end

这里的关键是dblp.expand读取整个子树(如<article>记录)并将其作为参数传递给工厂进行进一步处理。这是正确的方法吗?

在工厂方法中,我然后使用类似高级XPath的表达式来提取元素的内容,如下所示。再次,这是否可行?

def first(root, node)
    x = root.find(node).first
    x ? x.content : nil
end

pub.pages   = first(node,'pages') # node contains expanded node from dblp.expand

3 个答案:

答案 0 :(得分:5)

处理大型XML文件时,应使用流解析器来避免将所有内容加载到内存中。有两种常见的方法:

  • 推送解析器,就像SAX一样,当你得到它们时,你会对遇到的标签作出反应(参见 tadman 回答)。
  • 拉解析器,您可以在其中控制XML文件中的“光标”,您可以使用简单的原语(如上/下等)移动。

我认为如果你想只检索一些字段,推送解析器很好用,但它们通常很难用于复杂的数据提取,并且通常使用case... when...构造实现

在我看来,pull解析器是基于树的模型和推送解析器之间的一个很好的选择。您可以在Dr. Dobb的期刊中找到nice article关于使用REXML的拉解析器。

答案 1 :(得分:1)

处理XML时,两个常见选项是基于树的,基于事件的。基于树的方法通常读取整个XML文档,并且可能消耗大量内存。基于事件的方法不使用额外的内存,但除非您编写自己的处理程序逻辑,否则不会执行任何操作。

基于事件的模型由SAX风格的解析器和衍生实现使用。

REXML示例:http://www.iro.umontreal.ca/~lapalme/ForestInsteadOfTheTrees/HTML/ch08s01.html

REXML:http://ruby-doc.org/stdlib/libdoc/rexml/rdoc/index.html

答案 2 :(得分:0)

我遇到了同样的问题,但我想我通过调用Node#remove来解决它!在展开的节点上。在你的情况下,我认为你应该做像

这样的事情
my_node = dblp.expand
[do what you have to do with my_node]
dblp.next
my_node.remove!

不确定为什么会这样,但是如果你看一下LibXML :: XML :: Reader#expand的源代码,就会有关于释放节点的评论。我猜测Reader#expand将节点关联到Reader,你必须调用Node #remove!释放它。

即使有这种黑客攻击,内存使用也不是很好,但至少它没有继续增长。