Многоуровневый разбор текста

В последний раз у меня возникла проблема: Разбор и структурирование текстового файла Теперь я представляю сложные условия. Например. У меня был текстовый файл со следующим содержанием:

Head 1
Subhead 1
a 10
b 14
c 88
Subhead 2
a 15
b 16
c 17
d 88
Subhead 3
a 55
b 36
c 87
Head 4
Subhead 1
r 32
t 55
s 79
r 22
t 88
y 53
o 78
p 90
m 44
Head 53
Subtitle 1
y 22
b 33
Subtitle 2
a 88
g 43
r 87
Head 33
Subhead 1 
z 11
d 66
v 88
b 69
Head 32
Subhead 1
n 88
m 89
b 88
Subhead 2
b 88
m 43

Теперь мне нужен текст структуры для следующей плоскости. Я хочу получить следующие данные:

Head 1, Subhead 1, c 88
Head 1, Subhead 2, d 88
Head 4, Subhead 1, t 88
Head 53, Subhead 2, a 88
Head 33, Subhead 1, v 88
Head 32, Subhead 1, n 88
Head 32, Subhead 1, b 88
Head 32, Subhead 2, b 88

То есть я хочу получить все строки с 88 указанием заголовка и подзаголовка.

Мои действия:

lines = File.open("file.txt").to_a
lines.map!(&:chomp) # remove line breaks

current_head = ""
res = []

lines.each do |line|
  case line
  when /Head \d+/
    current_head = line
  when /Subhead/
    sub = line
  when /\w{1} 88/
  num = line
    res << "#{current_head}, #{sub}, #{num}"
  end
end

puts res

Когда я использую этот метод, я получаю строку без значений NUM.

Возможно ли выполнить мою задачу средствами "случая когда"?


person Misha1991    schedule 28.02.2017    source источник


Ответы (2)


Переменные, объявленные внутри блока each, не сохраняются между итерациями. Когда итерация заканчивается, эти переменные исчезают, поэтому вы теряете предыдущее значение sub. Чтобы это исправить, переместите переменную sub во внешнюю область, инициализировав ее перед each, как вы это сделали с current_head:

current_head = ""
current_sub = ""
res = []

lines.each do |line|
  case line
  when /Head \d+/
    current_head = line
  when /Subhead/
    current_sub = line
  when /\w{1} 88/
  num = line
    res << "#{current_head}, #{current_sub}, #{num}"
  end
end

См. на repl.it: https://repl.it/GBKn

person Jordan Running    schedule 28.02.2017
comment
Спасибо за ваше решение! - person Misha1991; 01.03.2017

Если вы хотите сохранить переменные между двумя итерациями, вы можете использовать переменные экземпляра.

File.foreach — рекомендуемый способ чтения файла:

res = []
File.foreach("file.txt") do |line|
  line.chomp!
  case line
  when /Head \d+/
    @current_head = line
  when /Sub(head|title)/
    @sub = line
  when /\w 88/
    num = line
    res << "#{@current_head}, #{@sub}, #{num}"
  end
end
puts res
person Eric Duminil    schedule 28.02.2017
comment
Спасибо за ваше решение! foreach действительно удобен - person Misha1991; 01.03.2017