Статьи

Рубиновый трансмогрификатор, часть II

Графика ввода-вывода

Получение информации в Transmogrifier

В нашем последнем эпизоде мы преобразовали данные из одного формата в другой. Теперь вам нужно ввести данные в него, используя трансмогрификатор. Мы могли бы жестко закодировать имена файлов там, но это нас преследует. Давайте сделаем так, чтобы мы могли загрузить файл определения, файл данных и имя выходного файла.

Вам нужно будет прочитать файл данных CSV. Как вы читаете файл построчно в Ruby?

require 'csv'

File.open("def.txt") do |file|
  while line = file.gets
    puts line
  end
end

Вы можете сохранить файл. Я сохранил его как transmogrifier.rb

Вы можете пойти дальше и запустить его.

 $ ruby transmogrifier.rb
FIELD NAME      FORMAT
NAME            A(50)
ADDRESS1        A(50)
ADDRESS2        A(50)
CITY            A(50)
STATE           A(2)
ZIP             A(10)
CONTACT         A(50)
CONTACTPHONE    A(10)
ACCOUNTOPENED   9(8)

Оно работает. У нас есть данные, теперь нам нужно загрузить определения. В transmogrifier.rb добавьте код, чтобы сделать это. Помните, что делать?

 File.open("data.csv") do |file|
  while line = file.gets
    puts line
  end
end

Если вам любопытно идти вперед и запустить его.

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

Давайте выпишем тип, длину и имя поля. Имя поля начинается с начала строки
Мы могли бы разбить строку и поместить ее в массив. Поскольку файл использует пробелы, а не символы табуляции, для выравнивания мы можем использовать разделение.

 require 'csv'

definitions = Array.new

File.open("def.txt") do |file|
  while line = file.gets
    definitions = line.split()
    puts definitions[0]
    puts definitions[1]
  end
end
...

Это дает нам имя поля, но тип и длина все еще вместе. Вы думали о регулярных выражениях, чтобы найти тип и длину?

 require 'csv'

definitions = Array.new

File.open("def.txt") do |file|
  while line = file.gets
    definitions = line.split()
    field = definitions[0]
    type = definitions[1] =~ /9/? '9' : 'A'
    length = definitions[1].slice(1..definitions[1].length).gsub(/[^0-9]/, "")
    puts 'field => ' + field.upcase + ' type => ' + type + ' length => ' + length.to_s
  end
end

Давай, беги.

 $ ruby transmogrifier.rb
field => FIELD type => A length =>
field => NAME type => A length => 50
field => ADDRESS1 type => A length => 50
field => ADDRESS2 type => A length => 50
field => CITY type => A length => 50
field => STATE type => A length => 2
field => ZIP type => A length => 10
field => CONTACT type => A length => 50
field => CONTACTPHONE type => A length => 10
field => ACCOUNTOPENED type => 9 length => 8

Теперь мы куда-то добираемся. Три из четырех переменных, которые передаются в трансмогрификатор. Маниакальный смех.

Один Квестон.

Как вы думаете, вы получите данные в этот массив?

Что если мы бросим массив в хеш? Затем перенесите этот хеш в другой хеш, где ключом является имя поля, а данные взяты из первого хеша.

Все, что нам нужно сделать, это перебрать данные, найти ключ из хэша, который идет с этим столбцом, и отправить все это в трансмогрификатор. Здесь все просто.

Во-первых, перейдите и поместите данные определения в хеш. Затем добавьте это в хеш. Ради интереса, выведите последний хеш, чтобы мы могли его увидеть.

 require 'csv'

object_name = Hash.new
definitions = Array.new

File.open("def.txt") do |file|
  while line = file.gets
    definitions = line.split()
    field = definitions[0]
    type = definitions[1] =~ /9/? '9' : 'A'
    length = definitions[1].slice(1..definitions[1].length).gsub(/[^0-9]/, "")
    object_formatting = Hash.new
      object_formatting["type"] = type
      object_formatting["length"] = length
      object_formatting["field"] = field
      object_name[field.upcase] = object_formatting
  end
end
puts object_name

Запустите его и давайте посмотрим, что у нас есть.

 $ ruby transmogrifier.rb
{"FIELD"=>{"type"=>"A", "length"=>"", "field"=>"FIELD"}, "NAME"=>{"type"=>"A", "length"=>"50", "field"=>"NAME"}, "ADDRESS1"=>{"type"=>"A", "length"=>"50", "field"=>"ADDRESS1"}, "ADDRESS2"=>{"type"=>"A", "length"=>"50", "field"=>"ADDRESS2"}, "CITY"=>{"type"=>"A", "length"=>"50", "field"=>"CITY"}, "STATE"=>{"type"=>"A", "length"=>"2", "field"=>"STATE"}, "ZIP"=>{"type"=>"A", "length"=>"10", "field"=>"ZIP"}, "CONTACT"=>{"type"=>"A", "length"=>"50", "field"=>"CONTACT"}, "CONTACTPHONE"=>{"type"=>"A", "length"=>"10", "field"=>"CONTACTPHONE"}, "ACCOUNTOPENED"=>{"type"=>"9", "length"=>"8", "field"=>"ACCOUNTOPENED"}}
...

Прекрасный. Теперь вам нужно обработать файл данных. Давайте получим имя поля и данные для этого столбца

 CSV.foreach("data.csv", headers: true) do |data|
  data.headers.each do |field|
    puts field + ' => ' + data[field].to_s
  end
end

Оставайтесь на линии! Что это за вызов CVS.foreach ()? Давайте посмотрим на это .
Эта строка будет обрабатывать файл CSV и обрабатывать первую строку как заголовок, а не как данные.

Когда вы запускаете, вы должны выглядеть так

 name => Wonder widgets
address1 => 1600 Vassar Street
address2 =>
city => dallas
state => tx
zip => 75220
contact => Tim Smith
contactPhone => 214-555-1212
accountOpened => 12052001

name => Timmy's Bikes
address1 => 2723 Auburn Street
address2 => Building 3
city => Erie
state => PA
zip => 16508-1234
contact =>
contactPhone => 814-555-4321
accountOpened => 865289

Мы почти дома.

Теперь у нас есть данные и определения. Давайте отправим эту информацию в трансмогрификатор.

Когда мы перебираем данные, нам нужно найти соответствующий ключ из хеша, который мы сделали ранее. Когда мы сопоставим его, отправьте его в трансмогрификатор

 CSV.foreach("data.csv", headers: true) do |data|
  data.headers.each do |field|
    object_name.each do |key,value|
      if field.upcase == key
        field.upcase!
        line = transmogrifier(data[field].to_s, object_name[field]["length"].to_i, object_name[field]["type"], field)
        print line
      end
    end
  end
  puts ' '
  puts ' --------- '
end

Это сделает все, о чем мы говорили. Если бы мы написали тесты, как в первой части, мы бы поймали кучу ошибок. Что делать, если нет данных для столбца? Как мы получаем все данные в одной строке? Как мы сохраняем данные? Как мы передаем определения и имена файлов данных?

Вот что я придумал перед рефакторингом

 require 'csv'

def transmogrifier(data,len,type,column)

proper_formatted = ''

unless data.nil?
    case
    when data.length > len
      data = data.slice(0..(len-1))
    when data.length < len
      if type == "A"
        data = data.ljust(len)
      else
        data = data.rjust(len)
      end
    else
      if column == "ACCOUNTOPENED"
        data = data.slice(4..7)+data.slice(0..3)
      else
        data
      end
    end
    proper_formatted += data
  end
  proper_formatted += '|' # added pipes to see where the field ends
  proper_formatted
end

object_name = Hash.new

file = File.new(ARGV[0],"r")

definitions = Array.new

while (line = file.gets)
    definitions = line.split()
    field = definitions[0]
    type = definitions[1] =~ /9/? '9' : 'A'
    length = definitions[1].slice(1..definitions[1].length).gsub(/[^0-9]/, "")
    object_formatting = Hash.new
      object_formatting["type"] = type
      object_formatting["length"] = length
      object_formatting["field"] = field
      object_name[field.upcase] = object_formatting
  end
file.close

aFile = File.new(ARGV[2], "w")

CSV.foreach(ARGV[1], headers: true) do |data|
  data.headers.each do |field|
    object_name.each do |key,value|
      if field.upcase == key
        field.upcase!
        line = transmogrifier(data[field].to_s, object_name[field]["length"].to_i, object_name[field]["type"], field)
        aFile.write(line)
        print line  #so you can see something in the terminal window
      end
    end
  end
  aFile.write("n")
  puts ' '
  puts ' --------- '
end

aFile.close

Если вы запустите $ ruby transmogrifier.rb def.txt data.csv formated.txt
В папке, в которой вы работаете, должен быть новый файл с именем formated.txt.

А теперь иди и преобрази маниакальный смех.