C/400

98. 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進数
00BINARY(4)CCSID
44BINARY(4)変換代替
88BINARY(4)代用代替
12CBINARY(4)シフト状態代替
1610BINARY(4)入力の長さオプション
2014BINARY(4)混合データのエラー・オプション
2418CHAR(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 }                                                               
【解説】

このサンプルは文字「」(CCSID=5026)を UNICODE(CCSID=1208) に iconv を使って変換する
方法を示している。
([注意] CCSID=1399 は UNICODE ではない。UNICODE の漢字は3バイトによって
漢字の1文字を表現する。
CCSID=1399 は EBCDIC なので、やはり漢字は2バイトで表現する。
従ってCCSID=1399 は UNICODE とは何の関係もない。)

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