如何在Ruby中用Oga解析一个大的XML文件?

问题描述 投票:0回答:1

我想用Oga解析一个大的XML文件,到目前为止,我可以用下面的脚本解析1.5Gb的文件,但是当我把目标放在一个5.6Gb的文件上时,Ruby使用的内存变得很大(超过50Gb),即使在3天内我也无法得到解析结果,我使用MacOS 10.15.4,Ruby 2.7.0,内存16Gb。我使用的是MacOS 10.15.4,ruby 2.7.0,内存16Gb.我知道有其他方法可以使用不同的Gems,包括Nokogiri,但如果可能的话,我想知道如何在Oga中做到这一点。

require 'oga'
Dir.chdir __dir__

file_n = "uniprot_sprot.xml"

xml = File.open(file_n)
puts "opened data file"
document = Oga.parse_xml(xml)
puts "parsed all data"

document.xpath('uniprot/entry').each do |entry|
  ...
end
ruby xml
1个回答
0
投票

你可以使用SAX风格的解析器。由于SAX解析器不会从XML中创建文档,所以它们对于解析大型文档很有用。缺点是你需要自己跟踪状态。我从来没有使用过OGA来进行SAX解析,但我假设它将适合你的5GB XML。

这里是自带的例子。只要把它粘贴到文件中并运行它(后面的部分是 __END__ 将作为输入,在 DATA).

require "oga"

class PeopleHandler
  PERSON_PATH = ["xml", "people", "person"]
  ATTRIBUTE_PATH = ["xml", "people", "person", "attribute"]
  attr_reader :people

  def initialize
    @people = []
    @current_person = nil
    @current_path = []
  end

  def on_element(_namespace, name, attrs = {})
    current_path.push(name)
    if current_path == PERSON_PATH
      people.push({id: attrs["id"]})
    elsif current_path == ATTRIBUTE_PATH
      people.last[attrs["name"]] = attrs["value"]
    end
  end

  def after_element(_namespace, name)
    current_path.pop
  end

  private

  attr_reader :current_path, :current_person
end

handler = PeopleHandler.new

Oga.sax_parse_xml(handler, DATA.read)

p handler.people

# [{:id=>"12", "first-name"=>"Pascal", "country"=>"Switzerland"}, {:id=>"13", "first-name"=>"Fred", "country"=>"Sweden"}, {:id=>"45", "first-name"=>"Karl", "country"=>"Hungary"}]

__END__
<xml>
  <people>
    <person id="12">
      <attribute name="first-name" value="Pascal" />
      <attribute name="country" value="Switzerland" />
    </person>
    <person id="13">
      <attribute name="first-name" value="Fred" />
      <attribute name="country" value="Sweden" />
    </person>
    <person id="45">
      <attribute name="first-name" value="Karl" />
      <attribute name="country" value="Hungary" />
    </person>
</xml>

Sax解析器通过向处理程序发出事件来工作。在这里查看可用事件(被调用的方法)的列表。https:/github.comYorickPeterseogablobmasterlibogaxmlsax_parser.rb。

该示例使用了一个数组(current_path)来跟踪文档中的位置。也许在你的情况下不需要这样,元素名称就足够了。

如果一个 <person> 元素,我将一个Hash推送到我的人员列表中。然后对每个 <attribute> 元素,我增强了该哈希(people.last)与一些键值对。解析完成后,我有一个人的列表,其中包括 handler.people 我可以进一步处理。

这只是给你举个例子,说明SAX解析器的工作原理。

  • 也许你不需要跟踪路径,也许元素名就够了(即当你的元素有一个唯一的名字)。那么你可以避免跟踪数组中的位置。
  • 也许你不想建立一个项目的集合来进一步处理。这可能是你用使用SAX解析器所节省的内存来换取你需要的项的内存。相反,你可能想在拥有所有必要信息后再处理一个项目(可能是在 after_element),然后把它扔掉。

如果你想对代码的不同部分进行计时,你可以使用一个简单的解决方案。

定时可以做得很简单,以获得一个想法。

t1 = Time.now
operation_1
t2 = Time.now
operation_2
t3 = Time.now
puts "Operation 1 took: #{t2 - t1}"
puts "Operation 2 took: #{t3 - t2}"
© www.soinside.com 2019 - 2024. All rights reserved.