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コードでの数値データを表現できた。ブログに書くとさらっとだけど、この後にさらにベンダー出してる商用の暗号化ソフトとか、全銀クライアントとの格闘があって、終わった直後は放心というか憔悴してたと思う。