Разбор структурированного текстового файла в Ruby - PullRequest
2 голосов
/ 22 декабря 2009

Как мне легко разобрать документ с такой структурой

description
some line of text
another line of text
more lines of text

quality
3 47 88 4 4 4  4

text: type 1
stats some funny stats

description
some line of text2
another line of text2
more lines of text2

quality
1 2  4 6 7

text: type 1
stats some funny stats

.
.
.

В идеале я хотел бы получить массив хеш-структур, где каждый хеш представляет «раздел» документа и, вероятно, должен выглядеть так:

{: description => "некоторая строка текста другая строка текста больше строк текста ", : quality => "3 47 88 4 4 4 4", : текст => тип 1, : stats => "немного забавной статистики"}

Ответы [ 4 ]

7 голосов
/ 22 декабря 2009

Вы должны искать строки индикатора (описание, качество, текст и статистика) в цикле и заполнять хэш при обработке документа построчно.

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

UPDATE:

sections = []

File.open("deneme") do |f|
  current = {:description => "", :text => "", :quality => "", :stats => ""}
  inDescription = false
  inQuality = false

  f.each_line do |line|
    if inDescription
      if line.strip == ""
        inDescription = false
      else
        current[:description] += line
      end
    elsif inQuality
      current[:quality] = line.strip
      inQuality = false
    elsif line.strip == "description"
      inDescription = true
    elsif line.strip == "quality"
      inQuality = true
    elsif line.match(/^text: /)
      current[:text] = line[6..-1].strip
    elsif line.match(/^stats /)
      current[:stats] = line[6..-1].strip
      sections.push(current)
      current = {:description => "", :text => "", :quality => "", :stats => ""}
    end
  end
end
3 голосов
/ 22 декабря 2009

Регулярное выражение:

ary = str.scan(/description\n(.*?)\n\nquality\n(.*?)\n\ntext:([^\n]+)\nstats([^\n]+)/m).inject([]) do |n, (desc, qual, text, stats)|
  n << { :description => desc.gsub("\n", ' '), :quality => qual, :text => text, :stats => stats }
end
2 голосов
/ 22 декабря 2009

Одна хитрость при разборе заключается в чтении данных в режиме абзаца - порции за раз. Если ваши подразделы последовательно разделены двумя новыми строками (или если вы можете использовать предварительный процесс для навязывания такой последовательности), чтение абзаца может быть полезным.

Помимо специальной обработки, необходимой для подраздела 'text', приведенный ниже пример является довольно общим, требующим только объявления названия последнего подраздела.

# Paragraph mode.
$/ = "\n\n"

last_subsection = 'text'
data = []

until DATA.eof
    data.push({})
    while true
        line = DATA.readline

        # Determine which sub-section we are in.
        ss = nil
        line.sub!( %r"\A(\w+):?\s*" ) { ss = $1; '' }

        # Special handling: split the 'text' item into 2 subsections.
        line, data[-1]['stats'] = line.split(/\nstats +/, 2) if ss == 'text'

        data[-1][ss] = line
        break if ss == last_subsection
    end

    # Cleanup newlines however you like.
    data[-1].each_key { |k| data[-1][k].gsub!(/\n/, ' ') }
end

# Check
data.each { |d| puts; d.each { |k,v| puts k + ' => ' + v } }

__END__
# Data not shown here...
2 голосов
/ 22 декабря 2009

Ваш ввод выглядит довольно близко к YAML, поэтому я бы преобразовал ввод в действительный YAML (используя метод, подобный Can), а затем использовал бы стандартную библиотеку ruby ​​для его загрузки. Затем, когда ваш пользователь столкнется с чем-то, о чем он не подумал в своей блестящей разметке, скажите ему, чтобы он просто использовал YAML:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...