ASCII to EBCDIC(符号付き数値)

数年前、全銀協のフォーマットで且つ、EBCDICコードで相手側のシステムに送信しなければならないという案件と格闘していた。全銀協とは全国銀行協会の略で、全国の銀行は、ここが定めるプロトコルおよび、データフォーマットで振込情報のやりとりやらなんやらをしている。

ファームバンキングと全銀形式(全銀ファイルフォーマット) - Shoulder.jp

ASCIIコードからEBCDICコードへの変換

そもそも僕はウェッブケー一本でやってきたのでEBCDICコードをどうやってLinux上で生成するのかわからなかった。調べた結果、ddコマンドを使えば簡単にデータファイルの変換が出来る事がわかった。

dd if=./input.txt of=./output.txt conv=ebcdic

数値データはサイン(符号)付きで

ddコマンドで変換を掛ければそれで済むと思った。ところがEBCDICの数値データはサイン(符号)付きでなければ駄目と言われた。(振込情報には金額のデータがあったので)

「はぁ?」

EBCDICコードでは数値データを 表現する場合、最終桁の数値は8bitのウチ上位4ビットを符号を示すものにしなければならなかった。仕様書にはCOBOLで言う所のS9という型で表記されていた。

ddコマンドは単に文字列をEBCDICコードに変換するだけなので、符号付きの数値データを当然考慮してくれなくて超困った。

ASCIIコードの数値を強引に変換

  • 最終桁の上位4bitが違うという事は = ASCIIコード上では最終桁が数値(0-9)では無くなる事を意味する。
  • 正負によって上位4bitはの値は変わってくる。

以上の手がかりをもとに、バイナリエディタと、EBCDICコード表もとにEBCDICの符号付き数値がASCIIコードで表現された時には最終桁はどんな文字列になるのかを泣きながら調べた。

そして下記のような関数を書いた(当時はPHPで書いたけど)。そしてデータファイル(上の例で言えばinput.txt)を書き出す前に、数値データはこの関数を通して書き出すようにした。

def ascii2ebcdic_sign_number(number):
    plus_codes = {0:'{', 1:'A', 2:'B', 3:'C', 4:'D', 5:'E', 6:'F', 7:'G', 8:'H', 9:'I'}
    minus_codes = {0:'}', 1:'J', 2:'K', 3:'L', 4:'M', 5:'N', 6:'O', 7:'P', 8:'Q', 9:'R'}
    codes = minus_codes if number < 0 else plus_codes
    return str(abs(number))[:-1] + codes[abs(number) % 10]

print ascii2ebcdic_sign_number(1001)  #=> 100A
print ascii2ebcdic_sign_number(-1001) #=> 100J

こうやって変換した結果をddコマンドで一発変換すれば、EBCDICコードでの数値データを表現できた。ブログに書くとさらっとだけど、この後にさらにベンダー出してる商用の暗号化ソフトとか、全銀クライアントとの格闘があって、終わった直後は放心というか憔悴してたと思う。

余談

  • 実は数年前にもブログに書いたけど、それはもう無くなってしまったのでサルベージした。
  • もしかしたら僕みたいに経験も知識もなく、同じような状況に陥ってる人がいたら、少しでもその人の助けになるようにと思って書いた。
  • ddを使わなくても、Pythonで文字列をEBCDICに変換する事はできるだろう。けど符号付き数値を表現するためには結局上のような事をやらなければならないと思ってる。もしより良い方法があれば追記したいので教えてください。

「これを必要とする貴方の心が折れる前にココに辿り着く事を祈る」