JFP 開発ガイド

第 4 章 日本語文字コード変換

この章では、文字列の複数バイト表現とワイド文字表現の相互変換用インタフェースと、iconv() 汎用コード変換インタフェースを紹介し、その利用方法を説明します。

概説

Solaris では、テキストデータを複数バイト表現 (ファイルコードとも呼ぶ) の形でファイルに格納します。一方、アプリケーションの内部でテキストデータを取り扱う際は、第 2 章「国際化 API での日本語処理」第 3 章「日本語ロケールと文字分類」で紹介したように、ワイド文字表現 (プロセスコードとも呼ぶ) の形で行うと便利な場合があります。したがって、日本語データを扱うアプリケーションでは、複数バイト表現とワイド文字表現の相互変換を行う場面がしばしば生じます。XPG はこの用途の API を用意しており、アプリケーションでのさまざまな相互変換を支援します。

また、日本語テキストで使用される文字集合は同じでもエンコーディングが異なるために、アプリケーションがテキストデータとして正しく取り扱えない場合があります。アプリケーションが自らの責任でエンコーディングの変換を行うように開発することもできますが、その場合、変換可能なすべてのエンコーディングに関する情報がアプリケーションに含まれていなくてはなりません。また、新しいエンコーディングをサポートする場合には、アプリケーションの再コンパイルが必要になります。

このような事態を避けるためには、コード変換に関するルール、または変換サービスそのものをアプリケーション本体から切り離し、変換前のエンコーディングと変換後のエンコーディングを指定して、動的に変換サービスを呼び出します。XPG はこの用途の API を用意しており、汎用のコード変換を支援します。

複数バイト・ワイド文字の相互変換

表 4-1 に、主な複数バイト・ワイド文字の相互変換のための API を紹介します。このほか、printf(3C)scanf(3C) などのマニュアルページも参照してください。

表 4-1 複数バイト・ワイド文字相互変換 API

インタフェース名

作用

mbtowc(pwc,s,n)

s の先頭から最大 n バイト調べ、複数バイト 1 文字分をワイド文字表現にして pwc へ格納

mbstowcs(pwcs,s,n)

s の先頭から複数バイト文字列をワイド文字列に変換する。最大 n ワイド文字変換したら終了

wctomb(s,wc)

ワイド文字 wc を複数バイト表現に変換し s へ格納

wcstombs(s,pwcs,n)

pwcs からワイド文字列を複数バイト表現に変換しながら s に格納。変換した複数バイトの合計が最大 n バイトになれば終了

mblen(s,n)

s の先頭から最大 n バイト調べ複数バイト 1 文字分を構成するバイト数を返す

fgetwc(stream)

入力ストリームから 1 複数バイト分を読み込みワイド文字表現で返す 

ungetwc(wc, stream)

ワイド文字 wcstream へプッシュバックする

fgetws(ws,n,stream)

入力ストリーム stream から複数バイト文字列を読み込み、最大 n-1 ワイド文字分を ws に格納する

fputwc(wc,stream)

出力ストリーム stream へワイド文字 wc を出力

fputws(ws,stream)

出力ストリーム stream へワイド文字列 ws を出力

プログラム例

例 4-1 は、あるファイルに対して複数バイト・ワイド文字の相互変換 API を適用するプログラム例です。これらの API を使用する場合は、適切なヘッダーファイルを取り込む必要があります。たとえば mbtowc()mbstowcs()wctomb()wcstombs()mblen() を使用する場合は stdlib.h を取り込み、ungetwc()fgetws()fputwc()fputws() を使用する場合は wchar.h を取り込みます。さらに、処理の最初の段階で setlocale() を呼び出し、動作ロケールを適切に設定する必要があります。


例 4-1 複数バイト・ワイド文字の相互変換

sun% cat my_mbwc.c

/*
 * Read lines from stdin and
 * count the number of chars
 * that belong to specific category.
 * Counting will stop if input reaches
 * EOF. It is assumed that each line
 * has at most BUFSIZ - 1 byte length.
 *
 * To categorize each chars, iswctype()
 * is used. Therefore, it is necessary
 * to convert the input multibyte buffer
 * to the wide char buffer. mbstowcs()
 * is called for that purpose.
 */

#include <stdio.h>
#include <locale.h>
#include <stdlib.h>
#include <wchar.h>

static char mbbuf[BUFSIZ];
static wchar_t wcbuf[BUFSIZ];

int
main(int argc, char *argv[])
{
        size_t retval;
        int i, alpha_char, ideo_char, kana_char, other_char;
        
        setlocale(LC_ALL, "");
        alpha_char = ideo_char = kana_char = other_char = 0;
        while(fgets(mbbuf, BUFSIZ, stdin) != NULL) {
                retval = mbstowcs(wcbuf, mbbuf, BUFSIZ);
                if (retval == (size_t)-1) {
                        fprintf(stderr, "Invalid char is found during mbstowcs()¥n");
                        exit(-1);
                }
                retval = wcslen((const wchar_t *)wcbuf);
                for (i = 0; i < retval; i++) {
                        if (iswctype(wcbuf[i], wctype("jisx0201r"))) {
	kana_char++;
} else if (iswctype(wcbuf[i], wctype("jkanji"))) {
	ideo_char++;
                        } else if (iswctype(wcbuf[i], wctype("alpha"))) {
                                alpha_char++;
                        } else {
                                other_char++;
                        }
                }
        }
        fprintf(stdout, "The input consist of¥n");
        fprintf(stdout, "%d Alphabetical chars,¥n", alpha_char);
        fprintf(stdout, "%d JIS X 0208/0212 Kanji chars,¥n", ideo_char);
        fprintf(stdout, "%d JIS X 0201 Kana chars and¥n", kana_char);
        fprintf(stdout, "%d other chars.¥n", other_char);
        return(0);
}
sun% cc -o my_mbwc my_mbwc.c
sun% cat file6
/* Here's the content of file3 */
新しいシステム*は現在のネットワーク*環境を変えることなく
インターネット*とのシームレス*な接続を可能にします。また
セキュリティ*の問題も新しい認証テクノロジー*を用いることで
アドミニストレータ*の負担を減らしています。
/* Here's the content of file5 */
ひらがなはかたかなに置換されます。
カタカナハヒラガナニ置換サレマス。
漢字、記号、全角alphabetや
JIS X 0201 カナナドハ* 置換 サレマセン*。
sun% ./my_mbwc < file6
The input consist of
54 Alphabetical chars,
31 JIS X 0208/0212 Kanji chars,
56 JIS X 0201 Kana chars and
117 other chars.
                     


注意 - 注意 -

* の部分のカタカナは、半角カタカナになります。


汎用コード変換

汎用コード変換の API は、表 4-2 のとおりです。変換前エンコーディングと変換後エンコーディングを指定する名称と指定の意味については、 iconv_ja(5) のマニュアルページと、『JFP ユーザーズガイド』の第 6 章を参照してください。

表 4-2 汎用コード変換 API

インタフェース名

作用

iconv_open()

変換元コードと変換先コードから変換に必要な情報を得る 

iconv()

得られた情報をもとに実際の変換を行う 

iconv_open()

変換に必要だった情報を解放する 

プログラム例

例 4-2 では、iconv() インタフェースを使用して iconv(1) コマンドのサブセットに相当するフィルタを作成します。これらの API を使用する場合は、iconv.h ヘッダーファイルを取り込む必要があります。一般的な国際化 API と異なり、iconv() では、変換前と変換後の文字集合とエンコーディングに関する情報を変換記述子を通して取得します。したがって、次のプログラム例でも setlocale() は呼び出されていません。


例 4-2 汎用コード変換

sun% cat my_iconv.c

/*
 * Read lines from a stdin and convert the encoding.
 * It is assumed that each line has at most BUFSIZ - 1
 * byte length.
 * Both of source and destination encodings are passed
 * from the command line.
 *
 * Note:        Calling iconv() itself doesn't need to call
 *              setlocale() in advance.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <iconv.h>
int
main(int argc, char *argv[])
{
        iconv_t icv_hook;
        char in_buf[BUFSIZ];
        char out_buf[BUFSIZ];
        char *inp;
        char *outp;
        char *from_code;
        char *to_code;
        extern char *optarg;
        extern int optind;
        size_t ret_val;
        size_t in_buf_left;
        size_t out_buf_left;
        int i;
        
        if (argc != 5) {
                fprintf(stderr, "usage: %s -f  -t ¥n", argv[0]);
                exit(-1);
        }
        while ((i = getopt(argc, argv, "f:t:")) != EOF) 
                switch (i) {
                case `f':
                        from_code = optarg;
                        break;
                case `t':
                        to_code = optarg;
                        break;
                default:
                        fprintf(stderr, "usage: %s -f  -t ¥n", argv[0]);
                        exit(-1);
                }
        icv_hook = iconv_open(to_code, from_code);
        if (icv_hook == (iconv_t)-1) {
                perror("iconv_open()");
                exit(-1);
        }
        

        i = 0;
        while(fgets(in_buf, BUFSIZ, stdin) != NULL){
                if (!in_buf[0]) {
                        perror("fgets()");
                        exit(-1);
                }
                i++;
                memset(out_buf, 0, BUFSIZ);
                in_buf_left = strlen(in_buf);
                out_buf_left = BUFSIZ;
                inp = in_buf;
                outp = out_buf;
                errno = 0;
                ret_val = iconv(icv_hook,
                                (const char **)inp, in_buf_left, outp, out_buf_left);
                if (ret_val == (size_t)-1) {
                        if (errno == EILSEQ)
                                perror("EILSEQ");
                        else if (errno == E2BIG)
                                perror("E2BIG");
                        else if (errno == EINVAL)
                                perror("EINVAL");
                        fprintf(stderr, "Line number is %d¥n", i);
                        exit(-1);
                }
                write(STDOUT_FILENO, out_buf, (BUFSIZ - out_buf_left));
        }
        iconv_close(icv_hook);
        return(0);
}
sun% cat file3
新しいシステム*は現在のネットワーク*環境を変えることなく
インターネット*とのシームレス*な接続を可能にします。また
セキュリティ*の問題も新しい認証テクノロジー*を用いることで
アドミニストレータ*の負担を減らしています。
sun% cc -o my_iconv my_iconv.c
sun% cat file3 | ./my_iconv -f eucJP -t PCK | ./my_iconv -f PCK -t eucJP
新しいシステム*は現在のネットワーク*環境を変えることなく
インターネット*とのシームレス*な接続を可能にします。また
セキュリティ*の問題も新しい認証テクノロジー*を用いることで
アドミニストレータ*の負担を減らしています。
                


注意 - 注意 -

* の部分のカタカナは、半角カタカナになります。