ユニコード (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 #include <stdio.h> 0002.00 #include <stdlib.h> 0003.00 #include <string.h> 0004.00 #include <qtqiconv.h> 0005.00 #include <errno.h> 0006.00 0007.00 #define TRUE 0 0008.00 #define FALSE -1 0009.00 #define UTF8 1208 0010.00 0011.00 void main(void){ 0012.00 char ebcbuf[128], unibuf[128]; 0013.00 iconv_t cd; 0014.00 QtqCode_T fromcode, tocode; 0015.00 size_t inbyte, outbyte, rtnbyte; 0016.00 char* source, *target; 0017.00 int i, len; 0018.00 0019.00 printf("** TESTICONV : iconv による Unicode 変換テスト **\n"); 0020.00 getchar(); 0021.00 memset(&fromcode, 0, sizeof(fromcode)); 0022.00 memset(&tocode, 0, sizeof(tocode)); 0023.00 fromcode.CCSID = 5026; 0024.00 tocode.CCSID = UTF8; 0025.00 /*( 文字「A」を UNICODE に変換する )*/ 0026.00 printf(" 文字「A」を UNICODE に変換する \n"); 0027.00 memset(ebcbuf, 0, sizeof(ebcbuf)); 0028.00 ebcbuf[0] = 0x0e; 0029.00 ebcbuf[1] = 0x42; 0030.00 ebcbuf[2] = 0xc1; /* 倍角の A */ 0031.00 ebcbuf[3] = 0x0f; 0032.00 inbyte = strlen(ebcbuf); 0033.00 for(i = 0; i<inbyte; i++){/*for-loop*/ 0034.00 printf("ebcbuf[%d] = 0x%02x\n", i, ebcbuf[i]); 0035.00 }/*for-loop*/ 0036.00 /*(1) 変換ハンドルを作成 */ 0037.00 cd = QtqIconvOpen(&tocode, &fromcode); 0038.00 if(cd.return_value == FALSE){ 0039.00 printf("[ERR] QtqIconvOpen errno = %d\n", errno); 0040.00 getchar(); 0041.00 exit(-1); 0042.00 } 0043.00 source = ebcbuf; 0044.00 target = unibuf; 0045.00 inbyte = strlen(ebcbuf); 0046.00 outbyte = sizeof(unibuf); 0047.00 /*(2) 変換を実行 */ 0048.00 rtnbyte = iconv(cd, &source, &inbyte, &target, &outbyte); 0049.00 if(rtnbyte < 0){ 0050.00 printf("failed in iconv: %s\n", strerror(errno)); 0051.00 getchar(); 0052.00 iconv_close(cd); 0053.00 exit(-1); 0054.00 } 0055.00 printf(" 文字「A」のユニコード変換結果 \n"); 0056.00 for(i = 0; i<strlen(unibuf); i++){/*for-loop*/ 0057.00 printf("unibuf[%d] = 0x%02x\n", i, unibuf[i]); 0058.00 }/*for-loop*/ 0059.00 getchar(); 0060.00 /*(3) 変換ハンドルをクローズ */ 0061.00 iconv_close(cd); 0062.00 }
【解説】
このサンプルは文字「A」(CCSID=5026)を UNICODE(CCSID=1208) に iconv を使って変換する
方法を示している。
([注意] CCSID=1399 は UNICODE ではない。UNICODE の漢字は3バイトによって
漢字の1文字を表現する。
CCSID=1399 は EBCDIC なので、やはり漢字は2バイトで表現する。
従ってCCSID=1399 は UNICODE とは何の関係もない。)
最初に「A」とは EBCDIC では
0028.00 ebcbuf[0] = 0x0e; 0029.00 ebcbuf[1] = 0x42; 0030.00 ebcbuf[2] = 0xc1; /* 倍角の A */ 0031.00 ebcbuf[3] = 0x0f;
である。
0023.00 fromcode.CCSID = 5026; 0024.00 tocode.CCSID = UTF8;
によって CCSID=5026 から UTF-8(CCSID=1208) への変換であることを
0037.00 cd = QtqIconvOpen(&tocode, &fromcode);
によって宣言して変換識別子(ハンドル) cd を取得する。
このハンドル cd を使って
0048.00 rtnbyte = iconv(cd, &source, &inbyte, &target, &outbyte);
によって 変換を実行すれば実行結果は target に収められ変換バイト数は rtnbyte である。
変換結果を
0056.00 for(i = 0; i<strlen(unibuf); i++){/*for-loop*/ 0057.00 printf("unibuf[%d] = 0x%02x\n", i, unibuf[i]); 0058.00 }/*for-loop*/
によって表示したら
0061.00 iconv_close(cd);
によって変換ハンドルもクローズして終了する。
【まとめ】
API: iconv のパラメータはポインターのポインターを使用しているため実行サンプルが
ないと実際にどのように記述すれば動作するのかが、なかなかわかりづらい。
しかし UNICODE がこれほど急に普及してきた時代背景を考えると EBCDIC/UNICODE の
変換は重要であり必須であると言える。
また国際言語化にとっても iconv の使い方をマスターしておくべきであろう。
最後にすべての CCSID の間を iconv だけですべて変換可能か? というとそういうわけでは
ないことを知ってして欲しい。
異なる CCSID の間の変換テーブルはべて System i に導入されているが、なかには
テーブルが用意されていないものがあり、これは QtqIconvOpen でエラーとなって
変換することはできない。
ただし QDCXLATE に比べて iconv のほうが実行速度も速く、変換精度も高いように思える。