Ваша строка 1 , слегка измененная:
data = <<END
MO SCGR SC RSITE ALARM_SITUATION
RXOTG-59 59 0 EK0322 ABIS PATH FAULT
RXOCF-59 EK0322 LOCAL MODE
RXOTRX-59-0 4 EK0322 LOCAL MODE
RXOTRX-59-1 EK0322 LOCAL MODE
RXOTRX-59-4 0
RXOTRX-59-5 1 3 EK0322 LOCAL MODE
RXOTRX-59-8 EK0322 LOCAL MODE
RXOTRX-59-9 EK0322 LOCAL MODE
END
Эта строка очень похожа на структуру данных CSV, поэтому у нас может возникнуть соблазн преобразовать ее в строку CSV, что позволитМы должны использовать методы, предоставляемые CSV классом.
Преобразовать строку в строку CSV
Код
def convert_to_csv(data)
cols = data[/.+?\n/].gsub(/ \S/).map { |s| Regexp.last_match.begin(0) }
data.each_line.map do |s|
cols.each { |i| s[i] = ',' if s.size > i+1 }
s.gsub(/ *, */, ',')
end.join
end
Преобразование строки
Теперь преобразуйте строку data
в строку CSV.
csv_data = convert_to_csv(data)
puts csv_data
MO,SCGR,SC,RSITE,ALARM_SITUATION
RXOTG-59,59,0,EK0322,ABIS PATH FAULT
RXOCF-59,,,EK0322,LOCAL MODE
RXOTRX-59-0,4,,EK0322,LOCAL MODE
RXOTRX-59-1,,,EK0322,LOCAL MODE
RXOTRX-59-4,,0
RXOTRX-59-5,1,3,EK0322,LOCAL MODE
RXOTRX-59-8,,,EK0322,LOCAL MODE
RXOTRX-59-9,,,EK0322,LOCAL MODE
Объяснение
Шаги выполняются следующим образом.
s = data[/.+?\n/]
#=> "MO SCGR SC RSITE ALARM_SITUATION\n"
e0 = s.gsub(/ \S/)
#=> #<Enumerator: "MO ... ALARM_SITUATION\n":gsub(/ \S/)>
cols = e0.map { Regexp.last_match.begin(0) - 1 }
#=> [17, 23, 34, 50]
e1 = data.each_line
#=> #<Enumerator: "MO ... LOCAL MODE\n":each_line>
a = e1.map do |s|
cols.each { |i| s[i] = ',' if s.size > i+1 }
s.gsub(/ *, */,',')
end
#=> ["MO,SCGR,SC,RSITE,ALARM_SITUATION\n",
# "RXOTG-59,59,0,EK0322,ABIS PATH FAULT\n",
# ...
# "RXOTRX-59-9,,,EK0322,LOCAL MODE\n"]
a.join
#=> < return value above >
Давайте подробнее рассмотрим вычисление a
.Сначала переменная блока s
назначается первому элементу, сгенерированному перечислителем e1
:
s = e1.next
#=> "MO SCGR SC RSITE ALARM_SITUATION\n"
Затем выполняется вычисление блока:
cols.each { |i| s[i] = ',' }
s #=> "MO ,SCGR ,SC ,RSITE ,ALARM_SITUATION\n"
s.gsub(/ *, */,',')
#=> "MO,SCGR,SC,RSITE,ALARM_SITUATION\n"
Регулярное выражениеиспользуется с gsub
читает, «соответствует нулю или больше пробелов, за которыми следует запятая, а затем ноль или больше пробелов».
Когда короткая строка передается в блок, выполняется следующий расчет.
s = "RXOTRX-59-4 0"
s.size
#=> 25
cols
#=> [17, 23, 34, 50]
cols.each { |i| s[i] = ',' if s.size > i+1 }
s #=> "RXOTRX-59-4 , ,0"
s.gsub(/ *, */,',')
#=> "RXOTRX-59-4,,0"
Остальные элементы e1
обрабатываются аналогично.
Преобразование строки CSV в хэш
Теперь мы можем использовать методы CSV,Например, предположим, что мы хотим создать массив хэшей, ключи которого являются элементами заголовка, в нижнем регистре и преобразованы в символы, а значения "SCGR"
и "SC"
должны быть преобразованы в целые числа.Для этого мы используем метод класса CSV :: new , указав соответствующие значения для параметров метода.
Построим хэш
require 'csv'
CSV.new(csv_data, headers: true, header_converters: :symbol,
converters: :all).to_a.map(&:to_h)
#=> [{:mo=>"RXOTG-59", :scgr=>59, :sc=>0, :rsite=>"EK0322",
# :alarm_situation=>"ABIS PATH FAULT"},
# {:mo=>"RXOCF-59", :scgr=>nil, :sc=>nil, :rsite=>"EK0322",
# :alarm_situation=>"LOCAL MODE"},
# {:mo=>"RXOTRX-59-0", :scgr=>4, :sc=>nil, :rsite=>"EK0322",
# :alarm_situation=>"LOCAL MODE"},
# {:mo=>"RXOTRX-59-1", :scgr=>nil, :sc=>nil, :rsite=>"EK0322",
# :alarm_situation=>"LOCAL MODE"},
# {:mo=>"RXOTRX-59-4", :scgr=>nil, :sc=>0, :rsite=>nil,
# :alarm_situation=>nil},
# {:mo=>"RXOTRX-59-5", :scgr=>1, :sc=>3, :rsite=>nil"EK0322",
# :alarm_situation=>"LOCAL MODE"},
# {:mo=>"RXOTRX-59-8", :scgr=>nil, :sc=>nil, :rsite=>"EK0322",
# :alarm_situation=>"LOCAL MODE"},
# {:mo=>"RXOTRX-59-9", :scgr=>nil, :sc=>nil, :rsite=>"EK0322",
# :alarm_situation=>"LOCAL MODE"}]
Пояснение
Шаги следующие.
csv = CSV.new(csv_data, headers: true, header_converters: :symbol,
converters: :all)
#=> <#CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:",
# " row_sep:"\n" quote_char:"\"" headers:true>
a = csv.to_a
#=> [#<CSV::Row mo:"RXOTG-59" scgr:59 sc:0 rsite:"EK0322" alarm_situation:"ABIS PATH FAULT">,
# #<CSV::Row mo:"RXOCF-59" scgr:nil sc:nil rsite:"EK0322" alarm_situation:"LOCAL MODE">,
# ...
# #<CSV::Row mo:"RXOTRX-59-9" scgr:nil sc:nil rsite:"EK0322" alarm_situation:"LOCAL MODE">]
a.map(&:to_h)
#=> < hash shown above >
1 Для запуска кода вам нужно будет сделать отступ в этом heredoc (или изменитьпервая строка data = <<-END.lines.map(&:lstrip).join
).