I have a group of files which I have to download from a legacy Cobol system each night. I convert these files from binary data files into MySql tables.
I wrote a Ruby program to do this using BinData for the individual file structures. There are several fields in each of the files which contain packed decimal data (Cobol COMP-3). The following code works in reading one of the binary files and I wrote code to convert the field amt1
to a floating point decimal field.
The problem with this code is that for each packed field I must repeat the code for field conversion and even worse hard code the number of decimal places for each field into the code (see the commented code in program).
Example of code:
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
How do I create my own data type primitive called pkddec
, which takes a read_length
and dec_pos
parameter and create a class PackedDecimal << BinData ::Primitive
?
Actually I cannot take credit for answering my question but give a BIG thanks to Dion Mendel the creator of "BinData" for answering this question in the support on Ruby Forge. I submitted the question late last night at close to midnight Chicago time and awoke this morning to find the answer from Dion Mendel which had been answered about 3 hours later. I wanted to share his answer with the community and show the working code.
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
Again, a BIG thanks to Dion Mendel
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.