ユニコード (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 を利用するしかないのである。
■ QtqIconvOpen : コード変換の割り振り API
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) |
予約済み |
■ iconv() : コード変換 API
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 ともにポインターのポインターを定義する仕様になっている
ことに注意すること。
■ iconv_close(): コード変換割り振り解放 API
int iconv_close(iconv_t cd)
iconv_close() 関数は 変換識別子 cd をクローズする。
【サンプル : TESTICONV】
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 に比べて conv のほうが実行速度も速く、変換精度も高いように思える。
