2012-11-05 30 views
2

我有一組文件,我必須從傳統的Cobol系統 每個晚上下載。我將這些文件從二進制數據文件轉換爲 MySql表。使用BinData從二進制數據創建用戶定義的基元類型?

我寫了一個Ruby程序,使用BinData爲個人 文件結構做到這一點。每個文件中有幾個字段包含 壓縮的十進制數據(Cobol COMP-3)。以下代碼在讀取二進制文件中的一個 時起作用,並且我寫了代碼將字段amt1轉換爲 浮點十進制字段。

這段代碼的問題是,每個包裝領域我必須重複進行實地轉換 代碼更糟糕的硬編碼(見程序註釋代碼)小數 每個字段到代碼的數量。代碼

例子:

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_pos參數,並創建一個class PackedDecimal << BinData ::Primitive

回答

1

其實我不能回答我的問題,但是要感謝Dion Mendel「BinData」的創建者在Ruby Forge支持中回答這個問題。我昨晚在芝加哥時間午夜左右提交了這個問題,今天早上醒來發現了Dion Mendel的答案,這個答案大約在3個小時後得到了答覆。我想與社區分享他的答案並顯示工作代碼。

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 

同樣,一個大感謝翁孟德爾

相關問題