Давайте сначала создадим ваш файл.
str = <<~_
#
# Report
#---------------
Date header1 header2 header3 header4
20200 jdk;df 4543 $8333 4387
20200 jdk 5004 $945876 67
_
fin_name = 'in'
File.write(fin_name, str)
#=> 223
Для чтения этого файла необходимо решить две проблемы, используя метод SmarterCSV: : процесс . Во-первых, комментарии - строки, начинающиеся с восьмиугольника ('#'
) - и пустые строки должны быть пропущены. Во-вторых, разделитель полей не является строкой фиксированной длины.
Первая из этих проблем может быть решена путем установки значения параметра ключа process
':comment_regexp
в регулярное выражение:
:comment_regexp => /\A#|\A\s*\z/
, которое читается как «соответствует восьмиграннику» в начале строки (\A
является якорем начала строки) или (|
) соответствует строке, содержащей ноль или более пробельных символов (\s
является пробельным символом и \z
является концом of-string anchor) ".
К сожалению, SmarterCSV
не способен работать с разделителями полей переменной длины. У него есть опция :col_sep
, но его значение должно быть строкой, а не регулярным выражением.
Поэтому мы должны предварительно обработать файл перед использованием SmarterCSV
, хотя это не сложно. Пока мы находимся, мы можем также удалить знаки доллара и использовать запятые для разделителей полей. 1
fout_name = 'out.csv'
fout = File.new(fout_name, 'w')
File.foreach(fin_name) do |line|
fout.puts(line.strip.gsub(/\s+\$?/, ',')) unless
line.match?(/\A#|\A\s*\z/)
end
fout.close
Давайте посмотрим на полученный файл.
puts File.read(fout_name)
отображает
Date,header1,header2,header3,header4
20200,jdk;df,4543,8333,4387
20200,jdk,5004,945876,67
Вот так должен выглядеть CSV-файл! Теперь мы можем использовать SmarterCSV
для этого файла без указания параметров:
SmarterCSV.process(fout_name)
#=> [{:date=>20200, :header1=>"jdk;df", :header2=>4543,
# :header3=>8333, :header4=>4387},
# {:date=>20200, :header1=>"jdk", :header2=>5004,
# :header3=>945876, :header4=>67}]
1. Я использовал IO :: foreach , чтобы прочитать файл построчно, а затем записать каждую обработанную строку, которая не является ни комментарием, ни пустой строкой, в выходной файл. Если файл не очень большой, мы можем вместо этого gulp поместить его в строку, изменить строку и записать полученную строку в выходной файл: File.write(fout_name, File.read(fin_name).gsub(/^#.*?\n|^[ \t]*\n|^[ \t]+|[ \t]+$|\$/, '').gsub(/[ \t]+/, ','))
. Первое регулярное выражение гласит: «сопоставлять строки, начинающиеся с восьмиугольника или строки, содержащие только пробелы и табуляции или пробелы и табуляции в начале строки или пробелы и табуляции в конце строки или знака доллара». Второй gsub
просто преобразует несколько знаков табуляции и пробелов в запятую.
File.new (fout_name, 'w') File.foreach (fin_name) do | line | fout.puts (line.strip.gsub (/ \ s + \ $? /, ',')), если только line.match? (/ \ A # | \ A \ s * \ z /) не заканчивается fout.close