我想用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
你可以使用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解析器的工作原理。
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}"