C ユーザーズガイド

複数バイト文字とワイド文字

最初に、ANSI C の国際化はライブラリ関数だけに影響がありました。しかし、国際化の最終段階 (複数バイト文字とワイド文字) は言語属性にも影響します。

アジア言語は複数バイト文字を必要とする

アジア言語を使用するコンピュータ環境における基本的な難しさは、膨大な数の表意文字を入出力しなければならないことです。通常のコンピュータアーキテクチャの制限内で動作するためには、このような表意文字はバイトシーケンスとして符号化します。関連するオペレーティングシステム、アプリケーションプログラム、および端末は、このようなバイトシーケンスを個々の表意文字として認識します。さらに、すべてのこのような符号化によって、通常の 1 バイト文字を表意文字のバイトシーケンスと混合できます。個々の表意文字を認識することがどのくらい困難であるかは、使用する符号化方式によって異なります。

「複数バイト文字」は、ANSI C の定義では、使用する符号化方式の種類に関係なく、表意文字を符号化するバイトシーケンスを示します。すべての複数バイト文字は「拡張文字セット」に属します。通常の 1 バイト文字は、単に複数バイト文字の特別なケースです。符号化に必要な唯一の条件は、どの複数バイト文字もヌル文字を符号化の一部として使用できないということです。

ANSI C では、プログラムのコメント、文字列リテラル、文字定数、およびヘッダー名がすべて複数バイト文字のシーケンスであると規定されています。

符号化の種類

符号化方式は 2 つの種類に分けることができます。1 つは、各複数バイト文字が自己識別性を持つ方式がそうです。つまり、どの複数バイト文字も簡単に 2 つの複数バイト文字の間に挿入できます。

もう 1 つは、特別なシフトバイトの存在が後続のバイトの解釈を変更する方式です。たとえば、あるキャラクタ端末で行描画モードを切り替えるために使用する方式です。このシフト状態依存符号化による複数バイト文字で書かれたプログラムの場合、ANSI C では、コメント、文字列リテラル、文字定数、およびヘッダー名の始まりと終わりがすべてシフトなし状態でなければならないと規定しています。

ワイド文字

複数バイト文字の処理で不都合が発生した場合は、すべての文字を一定のバイト数またはビット数にすることで解決できることがあります。このような文字セットには何千または何万もの表意文字があるため、これらすべてを保持するには、大きさが 16 ビットまたは 32 ビットの整数値を使用しなければなりません (完全な中国語には 65,000 以上もの表意文字があります)。ANSI C には、拡張文字セットのすべてを保持するために十分な大きさを持つ実装定義の整数型として、typedefwchar_t があります。

各ワイド文字には、それに対応する複数バイト文字があります (その逆もあります)。つまり、通常の 1 バイト文字に対応するワイド文字は、その 1 バイト値と同じ値を持つ必要があります (ヌル文字も含む)。しかし、マクロ EOFchar として表現できないように、マクロ EOF の値が wchar_t に格納できるかどうかは保証されていません。

変換関数

ANSI C では、複数バイト文字とワイド文字を管理するために、5 つのライブラリ関数を規定しています。

表 E-3 複数バイト文字の変換関数

mblen()  

次の複数バイト文字の長さ 

mbtowc()  

複数バイト文字からワイド文字に変換する 

wctomb()  

ワイド文字から複数バイト文字に変換する 

mbstowcs()  

複数バイト文字の文字列からワイド文字の文字列に変換する 

wcstombs()  

ワイド文字の文字列から複数バイト文字の文字列に変換する 

これらの関数のすべての動作は、ロケールによって異なります。setlocale() 関数」を参照してください。

アジア市場向けにコンパイルシステムを提供するベンダーが、より多くの文字列用関数を提供し、ワイド文字の文字列の処理が簡単になることが期待されます。しかし、ほとんどのアプリケーションプログラムでは、複数バイト文字とワイド文字間の変換は必要ありません。たとえば、複数バイト文字で読み取りと書き込みを行うプログラム (diff など) は、バイト単位での正確な一致を検査することだけが必要です。正規表現によるパターン一致を使用するより複雑なプログラム (grep など) は、複数バイト文字を理解する必要があります。しかし、複数バイト文字を理解する必要があるのは、正規表現を管理する関数だけです。プログラム grep 自身には、他の特別な複数バイト文字処理は必要ありません。

C 言語の機能

アジア言語環境においてプログラマがより柔軟にプログラムを組むために、ANSI C では、ワイド文字定数とワイド文字列リテラルを提供しています。この 2 つの形式は、直前に文字「L」の接頭辞が付くことを除き、通常の (ワイドでない) バージョンと同じです。

'x' 通常の文字定数

'¥' 通常の文字定数

L'x' ワイド文字定数

L'¥' ワイド文字定数

|abc¥xyz| 通常の文字列リテラル

L|abcxyz| ワイド文字列リテラル

複数バイト文字は、通常とワイドの両方のバージョンで有効です。表記文字 ¥ を生成するために必要なバイトシーケンスは符号化によって異なります。しかし、文字定数 '¥' が複数のバイトから構成される場合、'ab' が実装により定義されるのと同様に、その値は実装により定義されます。エスケープシーケンスを除き、通常の文字列リテラルには、引用符の間に指定されたものと同じバイト数 (指定したすべての複数バイト文字のバイト数も含む) が含まれます。

コンパイルシステムがワイド文字定数またはワイド文字列リテラルを検出したとき、各複数バイト文字は (mbtowc() 関数を呼び出したように) ワイド文字に変換されます。したがって、L'¥' の型は wchar_t です。abc¥xyz の型は長さが 8 の wchar_t の配列です。通常の文字列リテラルと同様に、各ワイド文字列リテラルは、値がゼロの余分な要素が追加されます。しかし、この要素は、ゼロの値を持つ wchar_t です。

通常の文字列リテラルが文字配列初期化の簡単な方法として使用できるのと同様に、ワイド文字列リテラルも wchar_t 配列を初期化するために使用できます。


wchar_t *wp = L"a¥z";
wchar_t x[] = L"a¥z";
wchar_t y[] = {L'a', L'¥', L'z', 0};
wchar_t z[] = {'a', L'¥', 'z', '¥0'};

上記の例では、3 つの配列 xyz と、wp が指す配列の長さは同じです。すべての配列は同じ値で初期化されます。

最後に、通常の文字列リテラルと同様に、隣接するワイド文字列リテラルは連結されます。しかし、通常の文字列リテラルとワイド文字列リテラルが隣接する場合、その動作は定義されていません。このような連結が受け付けられない場合、コンパイラはエラーを発行する必要はありません。