ユニコード (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 を利用するしかないのである。
RPG による iconv の使用方法は結構、難しいものとなるが、米国サイトを含めて RPG による
iconv の使用サンプルはどこにも紹介されていないのでここで初めて紹介することにした。
日本語を扱う我々にとってはこれから Unicode への変換は必須となってくる時代がすぐそこに
やってきていると言える。
恐らくここで紹介するサンプル・ソースがないと独力で iconv を利用するRPG を作成するのには
骨が折れるはずだ。
というのは iconv のパラメータはポインターを多用しており、かつAPI の仕様のとおりに
記述してもなかなか正常には動作してくれない。
しかも iconv のパラメータはポインターのポインターというRPG のプログラマーにとっては
ややこしくて理解できないような構造を必要としている。
RPG でもポインターを使う機会は少ないはずなので、それも含めて解説していくことになる。
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 H DATEDIT(*YMD/) 0002.00 F********** TESTICONV : iconv による Unicode 変換テスト *************** 0003.00 F* 0004.00 F* CRTRPGMOD QTEMP/TESTICONV SRCFILE(MYSRCLIB/QRPGLESRC) 0005.00 F* AUT(*ALL) 0006.00 F* CRTPGM MYLIB/TESTICONV MODULE(QTEMP/TESTICONV) 0007.00 F* BNDSRVPGM(QSYS/QTQICONV) ACRGRP(*NEW) 0008.00 F* 0009.00 F********************************************************************** 0010.00 0011.00 D QTQ_ICONV_OPEN PR 52A EXTPROC('QtqIconvOpen') 0012.00 D TOCODE * VALUE 0013.00 D FROMCODE * VALUE 0014.00 0015.00 D ICONV PR 4B 0 EXTPROC('iconv') 0016.00 D CD 52A VALUE 0017.00 D INPUT_P * VALUE 0018.00 D INPLEN * VALUE 0019.00 D OUTPUT_P * VALUE 0020.00 D OUTLEN * VALUE 0021.00 0022.00 D ICONV_CLOSE PR EXTPROC('iconv_close') 0023.00 D CD 52A VALUE 0024.00 0025.00 DICONV_T DS QUALIFIED 0026.00 D ICORV 1 4B 0 INZ(0) 0027.00 D ICOC 5 52B 0 DIM(00012) 0028.00 0029.00 DQTQ_CODE_T DS QUALIFIED 0030.00 D CCSID 1 4B 0 0031.00 D CONV 5 8B 0 INZ(0) 0032.00 D SUBSTI 9 12B 0 INZ(0) 0033.00 D SUBSTAT 13 16B 0 INZ(0) 0034.00 D INPLENOPT 17 20B 0 INZ(0) 0035.00 D ERROPT 21 24B 0 INZ(0) 0036.00 D RESERV 25 32A 0037.00 0038.00 D INPUT S 512A 0039.00 D INPUT_P S * INZ(%ADDR(INPUT)) 0040.00 D INPUT_PP S * 0041.00 D INPLEN S 10I 0 0042.00 D INPL_P S * INZ(%ADDR(INPLEN)) 0043.00 D OUTPUT S 512A 0044.00 D OUTPUT_P S * INZ(%ADDR(OUTPUT)) 0045.00 D OUTPUT_PP S * 0046.00 D OUTLEN S 10I 0 0047.00 D OUTL_P S * INZ(%ADDR(OUTLEN)) 0048.00 D CD DS LIKEDS(ICONV_T) 0049.00 D CD_P S * INZ(%ADDR(CD)) 0050.00 D RES S 4B 0 0051.00 D FROMCODE DS QUALIFIED 0052.00 D CCSID 1 4B 0 0053.00 D OTHER 5 32B 0 DIM(00007) 0054.00 D FROMCODE_P S * INZ(%ADDR(FROMCODE)) 0055.00 0056.00 D TOCODE DS QUALIFIED 0057.00 D CCSID 1 4B 0 0058.00 D OTHER 5 32B 0 DIM(00007) 0059.00 D TOCODE_P S * INZ(%ADDR(TOCODE)) 0060.00 0061.00 D TRUE S 4B 0 INZ(0) 0062.00 D FALSE S 4B 0 INZ(-1) 0063.00 0064.00 C*(1) 変換ハンドル CD を作成 */ 0065.00 C CLEAR FROMCODE 0066.00 C CLEAR TOCODE 0067.00 C EVAL FROMCODE.CCSID = 5026 0068.00 C EVAL TOCODE.CCSID = 1208 0069.00 C*----------------------------------------------------+ 0070.00 C EVAL CD = QTQ_ICONV_OPEN(TOCODE_P:FROMCODE_P) 0071.00 C IF CD.ICORV = FALSE 0072.00 C 'OPEN ERROR' DSPLY ANS 1 0073.00 C SETON LR 0074.00 C RETURN 0075.00 C ELSE 0076.00 C 'OPEN OK' DSPLY ANS 1 0077.00 C ENDIF 0078.00 C*----------------------------------------------------+ 0079.00 0080.00 C*(2) 変換を実行 0081.00 C MOVEL(P) ' A ' INPUT 0082.00 C Z-ADD 6 INPLEN 0083.00 C EVAL OUTLEN = %SIZE(OUTPUT) 0084.00 C EVAL INPUT_PP = %ADDR(INPUT_P) 0085.00 C EVAL OUTPUT_PP = %ADDR(OUTPUT_P) 0086.00 C*----------------------------------------------------+ 0087.00 C EVAL RES = ICONV(CD:INPUT_PP:INPL_P:OUTPUT_PP: 0088.00 C OUTL_P) 0089.00 C IF RES = FALSE 0090.00 C 'CONV ERROR' DSPLY ANS 1 0091.00 C SETON LR 0092.00 C RETURN 0093.00 C ELSE 0094.00 C 'CVT OK' DSPLY ANS 1 0095.00 C ENDIF 0096.00 C*----------------------------------------------------+ 0097.00 0098.00 C*(3) 変換ハンドルをクローズ */ 0099.00 C*----------------------------------------------------+ 0100.00 C CALLP ICONV_CLOSE(CD) 0101.00 C*----------------------------------------------------+ 0102.00 C 'CLOSE OK' DSPLY ANS 1 0103.00 C SETON LR 0104.00 C RETURN
このサンプルは文字「A
」(CCSID=5026)を UNICODE(CCSID=1208) に iconv
を使って変換する
方法を示している。
([注意] CCSID=1399 は UNICODE ではない。UNICODE の漢字は3バイトによって
漢字の1文字を表現する。
CCSID=1399 は EBCDIC なので、やはり漢字は2バイトで表現する。
従ってCCSID=1399 は UNICODE とは何の関係もない。)
最初に変換ハンドル CD
は
0025.00 DICONV_T DS QUALIFIED 0026.00 D ICORV 1 4B 0 INZ(0) 0027.00 D ICOC 5 52B 0 DIM(00012) : 0048.00 D CD DS LIKEDS(ICONV_T)
として定義されている。
0064.00 C*(1) 変換ハンドル CD を作成 */ 0065.00 C CLEAR FROMCODE 0066.00 C CLEAR TOCODE 0067.00 C EVAL FROMCODE.CCSID = 5026 0068.00 C EVAL TOCODE.CCSID = 1208
によって CCSID=5026 から UTF-8(CCSID=1208) への変換であることを宣言してから
0069.00 C*----------------------------------------------------+ 0070.00 C EVAL CD = QTQ_ICONV_OPEN(TOCODE_P:FROMCODE_P) 0071.00 C IF CD.ICORV = FALSE 0072.00 C 'OPEN ERROR' DSPLY ANS 1 0073.00 C SETON LR 0074.00 C RETURN 0075.00 C ELSE 0076.00 C 'OPEN OK' DSPLY ANS 1 0077.00 C ENDIF 0078.00 C*----------------------------------------------------+
によって変換識別子(ハンドル) CD
を取得する。
このハンドル CD
を使って
0080.00 C*(2) 変換を実行 0081.00 C MOVEL(P) ' A ' INPUT 0082.00 C Z-ADD 6 INPLEN 0083.00 C EVAL OUTLEN = %SIZE(OUTPUT) 0084.00 C EVAL INPUT_PP = %ADDR(INPUT_P) 0085.00 C EVAL OUTPUT_PP = %ADDR(OUTPUT_P) 0086.00 C*----------------------------------------------------+ 0087.00 C EVAL RES = ICONV(CD:INPUT_PP:INPL_P:OUTPUT_PP: 0088.00 C OUTL_P)
によって 変換を実行すれば実行結果は OUTPUT
に収められ変換バイト数は RES
である。
変換結果の完了後には
0098.00 C*(3) 変換ハンドルをクローズ */ 0099.00 C*----------------------------------------------------+ 0100.00 C CALLP ICONV_CLOSE(CD) 0101.00 C*----------------------------------------------------+
によって変換ハンドルもクローズして終了する。
API: iconv
のパラメータはポインターのポインターを使用しているため実行サンプルが
ないと実際にどのように記述すれば動作するのかが、なかなかわかりづらい。
ここで RPG プログラマーのためにポインターを簡単に解説すると
ポインターとは変数の番地(場所)を表す数値であり、すべての変数はあるポインター(番地)
から指定された長さの分だけ定義されている。
例えば、プロシージャー : QtqIconvOpen
の定義は
0011.00 D QTQ_ICONV_OPEN PR 52A EXTPROC('QtqIconvOpen') 0012.00 D TOCODE * VALUE 0013.00 D FROMCODE * VALUE
となっており、TOCODE
や FROMCODE
のタイプ *
は、これらの変数がポインターであることを表している。
0038.00 D INPUT S 512A 0039.00 D INPUT_P S * INZ(%ADDR(INPUT))
とは、変数 INPUT
に対して INPUT_P
とは INPUT
の番地を示すポインターである。
IBM API マニュアルによれば QtqIconvOpen
の関数は
QtqIconvOpen(QtqCode_T* tocode, QtqCode_T* fromcode)
として定義されているが QtqCode_T* tocode
のように書かれているのは
パラメータ : tocode
はタイプ QtqCode_T
のポインターであることを *
によって示している。
また、iconv
のパラメータで char **inbuf
の **
の記述は inbuf
が inbuf
の番地を
示すポインターのポインターであることを示している。
従って
0038.00 D INPUT S 512A 0039.00 D INPUT_P S * INZ(%ADDR(INPUT)) 0040.00 D INPUT_PP S * : 0084.00 C EVAL INPUT_PP = %ADDR(INPUT_P)
のようにして INPUT_PP
は変数 INPUT
のポインター(INPUT_P
) のポインターとして
算出してから、これをパラメータとして実行しているのである。
RPG プログラマーはポインターのポインターでは面食らってしまうかも知れないが
iconv
を確実に動作させて Unicode に変換できるRPGソースであることは確かである。
さて、それはともかく UNICODE がこれほど急に普及してきた時代背景を考えると EBCDIC/UNICODE の
変換は重要であり必須であると言える。
また国際言語化にとっても iconv
の使い方をマスターしておくべきであろう。
最後にすべての CCSID の間を iconv
だけですべて変換可能か? というとそういうわけでは
ないことを知ってして欲しい。
異なる CCSID の間の変換テーブルはべて System i に導入されているが、なかには
テーブルが用意されていないものがあり、これは QtqIconvOpen
でエラーとなって
変換することはできない。
ただし QDCXLATE
に比べて iconv
のほうが実行速度も速く、変換精度も高いように思える。