Как узнать номер строки для узла, используя интерфейс считывателя Nokogiri? - PullRequest
3 голосов
/ 09 апреля 2011

Я пытаюсь написать скрипт Nokogiri, который будет обрабатывать XML для текстовых узлов, содержащих двойные кавычки ASCII («» »). Поскольку я хочу вывод, подобный grep, мне нужен номер строки и содержимое каждой строки Однако я не могу понять, как определить номер строки, с которой начинается элемент. Вот мой код:

require 'rubygems'
require 'nokogiri'

ARGV.each do |filename|
    xml_stream = File.open(filename)
    reader = Nokogiri::XML::Reader(xml_stream)
    titles = []
    text = ''
    grab_text = false
    reader.each do |elem|
        if elem.node_type == Nokogiri::XML::Node::TEXT_NODE
            data = elem.value
            lines = data.split(/\n/, -1);

            lines.each_with_index do |line, idx|
                if (line =~ /"/) then
                    STDOUT.printf "%s:%d:%s\n", filename, elem.line()+idx, line
                end
            end
        end
    end
end

elem.line () не работает.

1 Ответ

4 голосов
/ 09 апреля 2011

XML и анализаторы на самом деле не имеют понятия номеров строк. Вы говорите о физической структуре файла.

Вы можете играть в игру с анализатором, используя средства доступа, ищущие текстовые узлы, содержащие переводы строк и / или возврат каретки, но которые можно отбросить, поскольку XML допускает вложенные узлы.

require 'nokogiri'

xml =<<EOT_XML
<atag>
  <btag>
    <ctag 
      id="another_node">
      other text
    </ctag>
  </btag>
  <btag>
    <ctag id="another_node2">yet
                             another
                             text</ctag>
    </btag>
  <btag>
    <ctag id="this_node">this text</ctag>
  </btag>
</atag>
EOT_XML

doc = Nokogiri::XML(xml)

# find a particular node via CSS accessor
doc.at('ctag#this_node').text # => "this text"

# count how many "lines" there are in the document
doc.search('*/text()').select{ |t| t.text[/[\r\n]/] }.size # => 12

# walk the nodes looking for a particular string, counting lines as you go
content_at = []
doc.search('*/text()').each do |n|
  content_at << [n.line, n.text] if (n.text['this text'])
end
content_at # => [[14, "this text"]]

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


РЕДАКТИРОВАТЬ: Я просмотрел какой-то старый код, пролистал некоторые из документов Нокогири и внес изменения, которые были отредактированы выше. Он работает правильно, включая работу с некоторыми патологическими случаями. Нокогири FTW!

...