ユニコード (UTF-8) の使用が一般的になってきた現在、IBM の EBCDIC コードを
ユニコードに変換する必要のある機会も多くなってくるはずだ。
コード変換API と言えば QDCXLATE が良く知られているが QDCXLATE
は
EBCDIC/ASCII 間の変換しかサポートしていない。
すべての言語コードのあいだの変換を行なうAPI は、ここで紹介する iconv
である。
iconv 関数は UNIX の API としても良く知られているが IBM System i では
独自の使用方法があるので、それをここで紹介する。
iconv
による言語コードの変換には次の3段階のステップを必要とする。
(1) 変換ハンドルの作成
どのCCSID から どのCCSID への変換であることを宣言して変換ハンドルを生成する。
(2) 実際のコード変換
iconv
関数を使って変換ハンドルを指定して実際に変換を実行する。
(3) ハンドルのクローズ
使用済みの変換ハンドルをクローズする。
このステップを経ることが必要であることがIBM API マニュアルには明確に書かれていないので
当初は苦労すると思われる。
また QDCXLATE と iconv のちがいも API マニュアルには解説されていない。
ユニコードへの変換には iconv の利用が必要であるとも書かれていない。
実際はユニコードとの変換には iconv を利用するしかないのである。
iconv_t QtqIconvOpen(QtqCode_T* tocode, QtqCode_T* fromcode)
QtqIconvOpen()
関数は fromcode
で定義されているCCSID から tocode
として定義されている
CCSID へ文字コードを変換するための初期化を行なって変換識別子 iconv_t
を戻す。
API には iconv_open
という同じ機能の API も用意されていますが iconv_open
関数は正しく動作しない。
この QtqIconvOpen
関数を使用すること。
■ QtqCode_T の形式
オフセット | タイプ | フィールド | |
---|---|---|---|
10進数 | 16進数 | ||
0 | 0 | BINARY(4) | CCSID |
4 | 4 | BINARY(4) | 変換代替 |
8 | 8 | BINARY(4) | 代用代替 |
12 | C | BINARY(4) | シフト状態代替 |
16 | 10 | BINARY(4) | 入力の長さオプション |
20 | 14 | BINARY(4) | 混合データのエラー・オプション |
24 | 18 | CHAR(8) | 予約済み |
size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char** outbuf, size_t *outbytesleft)
iconv()
関数は入力文字列 : inbuf
の値を cd
として与えられた変換識別子に基づいて
ある CCSID から別の CCSID へ変換して変換結果の文字列を outbuf
に入れて
変換した文字数を size_t
として戻す。
ただし 入力inbuf
, 出力 outbuf
ともにポインターのポインターを定義する仕様になっている
ことに注意すること。
int iconv_close(iconv_t cd)
iconv_close()
関数は 変換識別子 cd
をクローズする。
0001.00 PGM 0002.00 /*---------------------------------------------------------*/ 0003.00 /* TESTICONV : iconv によるユニコード変換 */ 0004.00 /* */ 0005.00 /* CRTCLMOD QTEMP/TESTICONV SRCFILE(MYSRCLIB/QCLLESRC) */ 0006.00 /* AUT(*ALL) */ 0007.00 /* CRTPGM MYLIB/TESTICONV MODULE(QTEMP/TESTICONV) */ 0008.00 /* BNDSRVPGM(QSYS/QTQICONV) ACRGRP(*NEW) */ 0009.00 /* AUT(*ALL) */ 0010.00 /*---------------------------------------------------------*/ 0011.00 DCL VAR(&MSG) TYPE(*CHAR) LEN(132) 0012.00 DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) 0013.00 DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) 0014.00 DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) 0015.00 DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132) 0016.00 DCL VAR(&FROMCODE) TYPE(*CHAR) LEN(32) 0017.00 DCL VAR(&TOCODE) TYPE(*CHAR) LEN(32) 0018.00 DCL VAR(&BIN4) TYPE(*CHAR) LEN(4) 0019.00 DCL VAR(&RTN4) TYPE(*INT) 0020.00 DCL VAR(&CD) TYPE(*CHAR) LEN(52) 0021.00 DCL VAR(&CD_P) TYPE(*PTR) 0022.00 DCL VAR(&INPUT) TYPE(*CHAR) LEN(512) 0023.00 DCL VAR(&INPUT_P) TYPE(*PTR) 0024.00 DCL VAR(&OUTPUT) TYPE(*CHAR) LEN(512) 0025.00 DCL VAR(&OUTPUT_P) TYPE(*PTR) 0026.00 DCL VAR(&IN_BYTE) TYPE(*INT) 0027.00 DCL VAR(&IN_BYTE_P) TYPE(*PTR) 0028.00 DCL VAR(&OUT_BYTE) TYPE(*INT) 0029.00 DCL VAR(&RTN_BYTE) TYPE(*INT) 0030.00 DCL VAR(&TRUE) TYPE(*INT) VALUE(0) 0031.00 DCL VAR(&FALSE) TYPE(*INT) VALUE(-1) 0032.00 DCL VAR(&NULL28) TYPE(*CHAR) LEN(28) + 0033.00 VALUE(X'00000000000000000000000000000000000+ 0034.00 000000000000000000000') 0035.00 DCL VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00') 0036.00 MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR)) 0037.00 0038.00 /*(1) 変換ハンドルを作成 */ 0039.00 CHGVAR VAR(%BIN(&BIN4)) VALUE(5026) 0040.00 CHGVAR VAR(&FROMCODE) VALUE(&BIN4 *CAT &NULL28) 0041.00 CHGVAR VAR(%BIN(&BIN4)) VALUE(1208) 0042.00 CHGVAR VAR(&TOCODE) VALUE(&BIN4 *CAT &NULL28) 0043.00 CHGVAR VAR(&CD_P) VALUE(%ADDR(&CD)) 0044.00 CALLPRC PRC('QtqIconvOpen') PARM((&TOCODE) + 0045.00 (&FROMCODE)) RTNVAL(&CD) 0046.00 CHGVAR VAR(&BIN4) VALUE(%SST(&CD 1 4)) 0047.00 CHGVAR VAR(&RTN4) VALUE(%BIN(&BIN4)) 0048.00 IF COND(&RTN4 *EQ &FALSE) THEN(DO) 0049.00 CHGVAR VAR(&MSG) VALUE('iconv open error') 0050.00 GOTO SNDMSG 0051.00 ENDDO 0052.00 SNDPGMMSG MSG('iconv OPEN.') MSGTYPE(*DIAG) 0053.00 0054.00 /*(2) 変換を実行 */ 0055.00 CHGVAR VAR(&CD_P) VALUE(%ADDR(&CD)) 0056.00 CHGVAR VAR(&INPUT) VALUE('A' *CAT &NULL) 0057.00 CHGVAR VAR(&INPUT_P) VALUE(%ADDR(&INPUT)) 0058.00 CHGVAR VAR(&IN_BYTE) VALUE(6) 0059.00 CHGVAR VAR(&IN_BYTE_P) VALUE(%ADDR(&IN_BYTE)) 0060.00 CHGVAR VAR(&OUTPUT_P) VALUE(%ADDR(&OUTPUT)) 0061.00 CHGVAR VAR(&OUT_BYTE) VALUE(512) 0062.00 CALLPRC PRC('iconv') PARM((&CD *BYVAL) (&INPUT_P) + 0063.00 (&IN_BYTE) (&OUTPUT_P) (&OUT_BYTE)) + 0064.00 RTNVAL(&RTN_BYTE) 0065.00 IF COND(&RTN_BYTE *EQ &FALSE) THEN(DO) 0066.00 CHGVAR VAR(&MSG) VALUE('iconv cvt error') 0067.00 CALLPRC PRC('iconv_close') PARM((&CD)) 0068.00 GOTO SNDMSG 0069.00 ENDDO 0070.00 SNDPGMMSG MSG('iconv CONVERT SUCCESS') MSGTYPE(*DIAG) 0071.00 0072.00 /*(3) 変換ハンドルをクローズ */ 0073.00 CLOSE: 0074.00 CALLPRC PRC('iconv_close') PARM((&CD *BYVAL)) 0075.00 SNDPGMMSG MSG('iconv API CLOSED.') MSGTYPE(*DIAG) 0076.00 RETURN 0077.00 0078.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) MSG(&MSG) + 0079.00 MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) + 0080.00 MSGFLIB(&MSGFLIB) 0081.00 SNDMSG: 0082.00 IF COND(&MSGID *NE ' ') THEN(DO) 0083.00 SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + 0084.00 MSGDTA(&MSGDTA) MSGTYPE(*ESCAPE) 0085.00 ENDDO 0086.00 ELSE CMD(DO) 0087.00 SNDPGMMSG MSG(&MSG) TOMSGQ(*TOPGMQ) MSGTYPE(*DIAG) 0088.00 ENDDO 0089.00 ENDPGM
このサンプルは文字「A
」(CCSID=5026)を UNICODE(CCSID=1208) に iconv
を使って変換する
方法を示している。
([注意] CCSID=1399 は UNICODE ではない。UNICODE の漢字は3バイトによって
漢字の1文字を表現する。
CCSID=1399 は EBCDIC なので、やはり漢字は2バイトで表現する。
従ってCCSID=1399 は UNICODE とは何の関係もない。)
最初に「A
」を EBCDIC で
0056.00 CHGVAR VAR(&INPUT) VALUE('A' *CAT &NULL)
としてセットしている。
0039.00 CHGVAR VAR(%BIN(&BIN4)) VALUE(5026) 0040.00 CHGVAR VAR(&FROMCODE) VALUE(&BIN4 *CAT &NULL28) 0041.00 CHGVAR VAR(%BIN(&BIN4)) VALUE(1208) 0042.00 CHGVAR VAR(&TOCODE) VALUE(&BIN4 *CAT &NULL28)
によって CCSID=5026 から UTF-8(CCSID=1208) への
変換であることを
0044.00 CALLPRC PRC('QtqIconvOpen') PARM((&TOCODE) + 0045.00 (&FROMCODE)) RTNVAL(&CD)
によって宣言して変換識別子(ハンドル) &CD
を取得する。
このハンドル &CD
を使って
0062.00 CALLPRC PRC('iconv') PARM((&CD *BYVAL) (&INPUT_P) + 0063.00 (&IN_BYTE) (&OUTPUT_P) (&OUT_BYTE)) + 0064.00 RTNVAL(&RTN_BYTE)
によって 変換を実行すれば実行結果は &OUTPUT
に収められ変換バイト数は &RTN_BYTE
である。
変換結果を完了後にはを
0074.00 CALLPRC PRC('iconv_close') PARM((&CD *BYVAL))
によって変換ハンドルもクローズして終了する。
API: iconv
のパラメータはポインターのポインターを使用しているため実行サンプルが
ないと実際にどのように記述すれば動作するのかが、なかなかわかりづらい。
特に CL で iconv
を動作させるときに重要となるポイントが
0020.00 DCL VAR(&CD) TYPE(*CHAR) LEN(52)
の記述にあるように変換識別子 &CD
が 52バイトであることである。
( 米国サイトのサンプルでは &CD
を 32 バイトとして定義していたので、それでは実行時の
エラーとなってしまう。)
これは C言語を使って変換識別子の長さを調べてみて初めて 52バイトであることが
わかるのである。 IBM API 解説書だけでは CL で動作させることはできない。
それはともかく UNICODE がこれほど急に普及してきた時代背景を考えると EBCDIC/UNICODE の
変換は重要であり必須であると言える。
また国際言語化にとっても iconv
の使い方をマスターしておくべきであろう。
最後にすべての CCSID の間を iconv
だけですべて変換可能か? というとそういうわけでは
ないことを知ってして欲しい。
異なる CCSID の間の変換テーブルはべて System i に導入されているが、なかには
テーブルが用意されていないものがあり、これは QtqIconvOpen
でエラーとなって
変換することはできない。
ただし QDCXLATE
に比べて iconv
のほうが実行速度も速く、変換精度も高いように思える。