CL

78. iconv によるUnicode 変換

ユニコード (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
【解説】

このサンプルは文字「」(CCSID=5026)を UNICODE(CCSID=1208) に iconv を使って変換する

方法を示している。

([注意] CCSID=1399 は UNICODE ではない。UNICODE の漢字は3バイトによって

漢字の1文字を表現する。

CCSID=1399 は EBCDIC なので、やはり漢字は2バイトで表現する。

従ってCCSID=1399 は UNICODE とは何の関係もない。)

最初に「」を 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 のほうが実行速度も速く、変換精度も高いように思える。