2

レガシー COBOL システムから毎晩ダウンロードしなければならないファイルのグループがあります。これらのファイルをバイナリ データ ファイルから MySql テーブルに変換します。

個々のファイル構造に BinData を使用してこれを行う Ruby プログラムを作成しました。各ファイルには、パック 10 進数データ (COBOL COMP-3) を含むいくつかのフィールドがあります。amt1次のコードはバイナリ ファイルの 1 つを読み取る際に機能し、フィールドを浮動小数点 10 進フィールドに変換するコードを書きました。

このコードの問題は、パックされたフィールドごとにフィールド変換のコードを繰り返さなければならず、さらに悪いことに、各フィールドの小数点以下の桁数をコードにハードコーディングする必要があることです (プログラム内のコメント付きコードを参照)。

コードの例:

require 'bindata'
require 'bigdecimal'

class WangRec < BinData::Record
  string  :cust_no,         :read_length => 6
  string  :doc_date,        :read_length => 6
  string  :doc_no,          :read_length => 6
  string  :doc_type,        :read_length => 1
  string  :apply_to_no,     :read_length => 6
  string  :cust_no_alt,     :read_length => 6
  string  :apply_to_no_alt, :read_length => 6
  string  :doc_due_date,    :read_length => 6
  string  :amt1,            :read_length => 6 
  string  :amt2,            :read_length => 5
  string  :ref,             :read_length => 30
  string  :slsmn1,          :read_length => 3
  string  :slsmn2,          :read_length => 3
  string  :slsmn3,          :read_length => 3
  string  :amt3,            :read_length => 5
end

def packed(packed_field, dec_pos) 
  unpkd_field = packed_field.unpack('H12')
  num, sign = unpkd_field[0][0..-2], unpkd_field[-1]
  unless sign == 'f'
    amt = num.insert(0, '-')
  end

  if dec_pos > 0
    dec_amt = amt.insert((dec_pos + 1) * -1, '.')
  end

  return dec_amt.to_f 

end

wang_aropnfile = File.open('../data/wangdata/AROPNFIL.bin', 'rb')

count = 0

while !wang_aropnfile.eof?
  rec = WangRec.read(wang_aropnfile)

# The following line of code would have to be repeated for each
# packed field along with the decimal places
  amt1 = packed(rec.amt1, 2)

  puts "#{rec.cust_no} #{rec.doc_type} #{rec.doc_date} #{amt1}"

  count += 1
end

puts count

とパラメータpkddecを取り、を作成するという独自のデータ型プリミティブを作成するにはどうすればよいですか?read_lengthdec_posclass PackedDecimal << BinData ::Primitive

4

1 に答える 1

1

実際、私は私の質問に答えたことを称賛することはできませんが、Ruby Forge のサポートでこの質問に答えてくれた「BinData」の作成者である Dion Mendel に大きな感謝を捧げます。私は昨夜遅く、シカゴ時間の真夜中近くに質問を提出し、今朝起きて、約 3 時間後に回答された Dion Mendel からの回答を見つけました。彼の答えをコミュニティと共有し、動作するコードを示したいと思いました。

require 'bindata'
require 'bigdecimal'

class PkdDec < BinData::Primitive
  mandatory_parameter :length
  default_parameter   :dec_pos => 0

  string  :str, :read_length => :length

  def get
    str_length = eval_parameter(:length)
    dec_pos    = eval_parameter(:dec_pos)
    unpkd_field = str.unpack("H#{str_length * 2}").first
    num, sign = unpkd_field[0..-2], unpkd_field[-1]
    unless sign == 'f'
      num = num.insert(0, '-')
    end

    if dec_pos > 0
      dec_amt = num.insert((dec_pos + 1) * -1, '.')
    else
      dec_amt = num
    end

    return dec_amt.to_f 

  end

  def set(dec_val)

    #  Not concerned about going the other way
    #  Reverse the get process above

  end

end

class WangRec < BinData::Record
  string  :cust_no, :read_length => 6
  string  :doc_date, :read_length => 6
  string  :doc_no,  :read_length => 6
  string  :doc_type,  :read_length => 1
  string  :apply_to_no, :read_length => 6
  string  :cust_no_alt, :read_length => 6
  string  :apply_to_no_alt, :read_length => 6
  string  :doc_due_date,    :read_length => 6
  PkdDec  :amt1,            :length => 6,  :dec_pos => 2 
  PkdDec  :amt2,            :length => 5,  :dec_pos => 2
  string  :ref,             :read_length => 30
  string  :slsmn1,          :read_length => 3
  string  :slsmn2,          :read_length => 3
  string  :slsmn3,          :read_length => 3
  PkdDec  :amt3,            :length => 5,  :dec_pos => 2

end

wang_aropnfile = File.open('../data/wangdata/AROPNFIL.bin', 'rb')

count = 0

while !wang_aropnfile.eof?
  rec = WangRec.read(wang_aropnfile)
  puts "#{rec.cust_no} #{rec.doc_type} #{rec.amt1} #{rec.amt2} #{rec.amt3}"

  count += 1
end

puts count

繰り返しになりますが、Dion Mendel に多大な感謝を捧げます

于 2012-11-06T17:19:02.720 に答える