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

Chr*_*dig 4 ruby stream libxml-ruby

我想在Ruby中使用libxml 读取包含超过一百万个小书目记录(如)的大型XML文件<article>...</article>.我已经尝试将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
Run Code Online (Sandbox Code Playgroud)

这里的关键是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
Run Code Online (Sandbox Code Playgroud)

par*_*tic 5

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

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

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

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