Как я могу использовать Nokogiri :: XML :: Reader для анализа больших файлов XML? - PullRequest
12 голосов
/ 13 июля 2011

Я пытаюсь использовать Ruby's Nokogiri для анализа больших (1 ГБ или более) XML-файлов. Я тестирую код для файла меньшего размера, содержащего только 4 записи , доступные здесь . Я использую Nokogiri версии 1.5.0, Ruby 1.8.7 на Ubuntu 10.10. Поскольку я не очень хорошо понимаю SAX, я пытаюсь запустить Nokogiri :: XML :: Reader.

Моя первая попытка получить содержимое тега PMID выглядит следующим образом:

#!/usr/bin/ruby
require "rubygems"
require "nokogiri"

file   = ARGV[0]
reader = Nokogiri::XML::Reader(File.open(file))
p      = []
reader.each do |node|
  if node.name == "PMID"
    p << node.inner_xml
  end
end

puts p.inspect

Вот что я надеялся увидеть:

["21714156", "21693734", "21692271", "21692260"]

Вот что я на самом деле видел:

["21714156", "", "21693734", "", "21692271", "", "21692260", ""]

Кажется, по какой-то причине мой код находит или генерирует дополнительный пустой тег PMID для каждого экземпляра PMID. Либо это, либо inner_xml не работает, как я думал.

Буду признателен, если кто-нибудь подтвердит, что мой код и данные генерируют показанный результат, и подскажет, где я ошибаюсь.

1 Ответ

19 голосов
/ 13 июля 2011

Каждый элемент в потоке проходит как два события: одно, чтобы открыть элемент, и одно, чтобы закрыть его. Открытие мероприятия будет иметь

node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT

и событие закрытия будет иметь

node.node_type == Nokogiri::XML::Reader::TYPE_END_ELEMENT

Пустые строки, которые вы видите, являются просто событиями закрытия элемента. Помните, что при разборе SAX вы в основном проходите по дереву, поэтому вам нужно второе событие, которое сообщит вам, когда вы вернетесь назад и закроете элемент.

Вы, вероятно, хотите что-то похожее на это:

reader.each do |node|
  if node.name == "PMID" && node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
    p << node.inner_xml
  end
end

Или, возможно:

reader.each do |node|
  next if node.name      != 'PMID'
  next if node.node_type != Nokogiri::XML::Reader::TYPE_ELEMENT
  p << node.inner_xml
end

Или какой-то другой вариант.

...