#マルチバイト文字

この章では、C 言語が英語以外の文字を処理する方法について説明します。

Unicode の概要

C 言語が誕生したとき、英語の文字のみが考慮され、すべての文字を表すために 7 ビット ASCII コードが使用されました。 ASCII コードの範囲は 0 ~ 127 です。つまり、1 バイトで表現できる最大 100 文字以上しか表現できないため、char 型は 1 バイトしか占有しません。

ただし、英語以外の文字を扱う場合は、中国語だけでも 1 バイトでは不十分であり、文字セットは複数のバイトで表現する必要があります。

当初、国ごとに独自の文字エンコード方式があったため、複数の文字を混在させるのは不便でした。そのため、徐々に Unicode エンコードに統一され、すべての文字が 1 つの文字セットにまとめられました。

Unicode では、各文字にコード ポイントと呼ばれる番号があり、0 から 127 までの部分が ASCII コードと一致します。通常、「U+16 進コード ポイント」は、文字「A」を表す「U+0041」など、文字を表すために使用されます。

現在、Unicode エンコードには 100 万を超える文字が含まれており、コード ポイントの範囲は U+0000 ~ U+10FFFF です。 Unicode 文字セット全体を完全に表現するには、少なくとも 3 バイトが必要です。ただし、すべてのドキュメントでそれほど多くの文字が必要なわけではありません。たとえば、ASCII コードで十分な英語のドキュメントの場合、各文字が 3 バイトで表現される場合、ファイル サイズは 1 バイト表現の 3 倍になります。

さまざまな使用ニーズに適応するために、Unicode 標準委員会は、Unicode コード ポイントを表す 3 つの異なる表現方法を提供しています。

  • UTF-8: コード ポイントを表すために 1 ~ 4 バイトを使用します。文字が異なれば、占有するバイト数も異なります。
  • UTF-16: 文字 U+0000 から U+FFFF (ベース プレーンと呼ばれる) の場合、コード ポイントを表すために 2 バイトが使用されます。他の文字は 4 バイトを使用します。
  • UTF-32: コード ポイントを表すために 4 バイトが一律に使用されます。

その中で、UTF-8 は ASCII 文字 (U+0000 ~ U+007F) を表すのに 1 バイトしか使用せず、ASCII エンコードとまったく同じであるため、最も広く使用されています。

C 言語には、現在のシステムでサポートされているエンコーディングのバイト長を示す 2 つのマクロが用意されています。どちらのマクロもヘッダー ファイル limits.h で定義されます。

  • MB_LEN_MAX: サポートされる領域の最大バイト長。limits.h で定義されます。
  • MB_CUR_MAX: 現在の言語の最大バイト長。常に MB_LEN_MAX 以下であり、stdlib.h で定義されます。

文字表現方法

文字表現の本質は、各文字を整数にマップし、その整数に対応する文字をエンコード テーブルから取得することです。

C 言語では、整数の文字を表すさまざまな記述方法が提供されています。

  • \123: 文字を 8 進数値として表します。スラッシュの後には 3 桁の数字が必要です。
  • \x4D: 16 進数の文字を表し、\x の後に 16 進数の整数が続きます。
  • \u2620: Unicode コード ポイントの文字を表します (ASCII 文字には適用されません)。コード ポイントは 16 進数で表され、\u の後には 4 文字が必要です。
  • \U0001243F: Unicode コード ポイントの文字を表します (ASCII 文字には適用されません)。コード ポイントは 16 進数で表され、\U の後には 8 文字が必要です。
printf("ABC\n");
printf("\101\102\103\n");
printf("\x41\x42\x43\n");

上記の 3 行はすべて「ABC」を出力します。

printf("\u2022 箇条書き 1\n");
printf("\U00002022 箇条書き 1\n");

上の両方の行には「• Bullet 1」が出力されます。

マルチバイト文字の表現

C 言語のデフォルトでは、リテラルで表現できる基本文字のみが使用されます。他のすべての文字はコード ポイントで表現する必要があり、現在のシステムもコード ポイントのエンコード方式をサポートする必要があります。

いわゆる基本文字とは、@$ の 3 文字を除く、すべての印刷可能な ASCII 文字を指します。

したがって、英語以外の文字が出現する場合は、Unicode コード ポイント形式で記述する必要があります。

char* s = "\u6625\u5929";
printf("%s\n", s);

上記のコードは、中国語の単語「春」を出力します。

現在のシステムが UTF-8 エンコードされている場合は、リテラルを直接使用してマルチバイト文字を表すことができます。

char* s = "春";
printf("%s\n", s);

「\u + コード ポイント」および「\U + コード ポイント」の書き方は、「0x24」 ( $ )、0x40 (@)、および 0x60 ( )。

char* s = "\u0024\u0040\u0060";
printf("%s\n", s);

上記のコードは 3 つの Unicode 文字「@$`」を出力しますが、他の ASCII 文字はこの表現では表現できません。

プログラムの実行時に文字が正しく解釈されるようにするには、プログラム環境をローカライズされた環境に切り替えることが最善です。

setlocale(LC_ALL, "");

上記のコードでは、setlocale() を使用して、実行環境をシステムのローカライズされた言語に切り替えます。 setlocale() のプロトタイプはヘッダー ファイル locale.h で定義されています。詳細については、標準ライブラリのセクションの「locale.h」の章を参照してください。

以下のようにエンコード言語を指定することも可能です。

setlocale(LC_ALL, "zh_CN.UTF-8");

上記のコードは、プログラムの実行環境を中国語環境の UTF-8 エンコードに切り替えます。

C 言語では、接頭辞「u8」を使用してマルチバイト文字列のエンコード方式を UTF-8 として指定できます。

char* s = u8"スプリング";
printf("%s\n", s);

文字列にマルチバイト文字が含まれると、文字列内のバイト数が文字数に対応しなくなります。たとえば、文字列の長さが 10 バイトの場合、その文字列には 10 文字は含まれなくなり、7 文字、5 文字などのみが含まれる可能性があります。

setlocale(LC_ALL, "");

char* s = "春";
printf("%d\n", strlen(s)); // 6

上の例では、文字列 s には 2 文字しか含まれていませんが、strlen() によって返される結果は 6 であり、これら 2 つの文字が合計 6 バイトを占めることを示しています。

C 言語の文字列関数はシングルバイト文字に対してのみ有効であり、strtok()strchr()strspn()toupper()to lower( )isalpha() などは正しい結果を与えません。

ワイド文字

前のセクションのマルチバイト文字列では、各文字のバイト幅は可変です。このエンコード方法は便利ですが、文字列処理には適していないため、各文字が占めるバイト数を 1 つずつチェックする必要があります。したがって、この方法に加えて、C 言語では、ワイド文字と呼ばれる、幅が決まっているマルチバイト文字の格納方法も提供されています。

いわゆる「ワイド文字」とは、各文字が占めるバイト数が 2 バイトまたは 4 バイトに固定されていることを意味します。そうすれば、迅速に処理するのが簡単になります。

ワイド文字には別のデータ型 wchar_t があり、各ワイド文字はこの型です。これは整数型のエイリアスであり、現在の実装に応じて符号付きまたは符号なしの場合があります。この型の長さは 16 ビット (2 バイト) または 32 ビット (4 バイト) で、現在のシステムのすべての文字を保持するには十分です。これはヘッダー ファイル wchar.h で定義されます。

ワイド文字リテラルには「L」という接頭辞を付ける必要があります。そうしないと、C 言語はリテラルをナロー文字タイプとして扱います。

setlocale(LC_ALL, "");

wchar_t c = L'牛';
printf("%lc\n", c);

wchar_t* s = L"スプリング";
printf("%ls\n", s);

上記の例では、接頭辞 "L" は一重引用符の前にあり、ワイド文字を示し、printf() に対応するプレースホルダーは二重引用符の前にあり、ワイド文字を示します。 printf() に対応する文字列。プレースホルダーは %ls です。

ワイド文字列の末尾にもヌル文字がありますが、これはワイドヌル文字であり、複数バイトを占めます。

ワイド文字を処理するには、ワイド文字固有の関数を使用する必要があります。そのほとんどはヘッダー ファイル wchar.h で定義されています。

マルチバイト文字処理関数

###mblen()

mblen() 関数は、マルチバイト文字が占めるバイト数を返します。そのプロトタイプはヘッダー ファイル stdlib.h で定義されます。

int mblen(const char* mbstr, size_t n);

通常、最初のパラメータはマルチバイト文字列ポインタであり、2 番目のパラメータはチェックされるバイト数です。現在のシステムが占有している最大バイト数。通常は「MB_CUR_MAX」が使用されます。

戻り値は、文字が占めるバイト数です。現在の文字が空のワイド文字の場合は '0' を返し、現在の文字が有効なマルチバイト文字でない場合は '-1' を返します。

setlocale(LC_ALL, "");

char* mbs1 = "春";
printf("%d\n", mblen(mbs1, MB_CUR_MAX)); // 3

char* mbs2 = "abc";
printf("%d\n", mblen(mbs2, MB_CUR_MAX)); // 1

上記の例では、文字列「春」の最初の文字「春」は 3 バイトを占め、文字列「abc」の最初の文字「a」は 1 バイトを占めます。

wctomb()

wctomb() 関数 (ワイド文字からマルチバイトへ) は、​​ワイド文字をマルチバイト文字に変換するために使用されます。そのプロトタイプはヘッダー ファイル stdlib.h で定義されます。

int wctomb(char* s, wchar_t wc);

wctomb() は 2 つのパラメータを受け取ります。最初のパラメータはターゲットとしてのマルチバイト文字配列で、2 番目のパラメータは変換する必要があるワイド文字です。その戻り値は、マルチバイト文字ストレージによって占有されているバイト数、または変換を実行できない場合は -1 です。

setlocale(LC_ALL, "");

wchar_t wc = L'牛';
char mbStr[10] = "";

int nBytes = 0;
nBytes = wctomb(mbStr, wc);

printf("%s\n", mbStr); // 牛
printf("%d\n", nBytes);

上記の例では、wctomb() はワイド文字「cow」をマルチバイト文字に変換し、wctomb() の戻り値は、変換されたマルチバイト文字が 3 バイトを占めることを示します。

mbtowc()

mbtowc() はマルチバイト文字をワイド文字に変換するために使用されます。そのプロトタイプはヘッダー ファイル stdlib.h で定義されます。

int mbtowc(
   wchar_t* wchar、
   const char* mbchar、
   size_t カウント
);

3 つのパラメータを受け入れます。最初のパラメータはターゲットとしてのワイド文字ポインタ、2 番目のパラメータは変換されるマルチバイト文字ポインタ、3 番目のパラメータはマルチバイト文字のバイト数です。

戻り値はマルチバイト文字のバイト数、または変換が失敗した場合は -1 です。

setlocale(LC_ALL, "");

char* mbchar = "牛";
wchar_t トイレ;
wchar_t* pwc = &wc;

int nBytes = 0;
nBytes = mbtowc(pwc, mbchar, 3);

printf("%d\n", nBytes);
printf("%lc\n", *pwc); // 牛

上記の例では、mbtowc() はマルチバイト文字「cow」をワイド文字「wc」に変換し、戻り値は「mbchar」が占有するバイト数 (3 バイトを占有) になります。

wcstombs()

wcstombs() は、ワイド文字列をマルチバイト文字列に変換するために使用されます。そのプロトタイプはヘッダー ファイル stdlib.h で定義されます。

size_t wcstombs(
   char* mbstr、
   const wchar_t* wcstr、
   size_t カウント
);

最初のパラメータ mbstr はターゲットのマルチバイト文字列ポインタ、2 番目のパラメータ wcstr は変換されるワイド文字列ポインタ、3 番目のパラメータ count は最大値を格納するために使用されます。マルチバイト文字列内のバイト数。

変換が成功した場合、戻り値は、末尾の文字列終端文字を除いた、正常に変換されたマルチバイト文字列のバイト数になります。変換が失敗した場合は、「-1」が返されます。

以下に例を示します。

setlocale(LC_ALL, "");

チャーム[20];
wchar_t* wcs = L"スプリング";

int nBytes = 0;
nBytes = wcstombs(mbs, wcs, 20);

printf("%s\n", mbs); // 春
printf("%d\n", nBytes); // 6

上記の例では、wcstombs() はワイド文字列 wcs をマルチバイト文字列 mbs に変換します。戻り値 6 は、mbs に書き込まれる文字列が末尾を除いて 6 バイトを占めることを意味します。文字列ターミネータ。

wcstombs() の最初の引数が NULL の場合、変換が成功するために必要なターゲット文字列のバイト数を返します。

mbstowcs()

mbstowcs() は、マルチバイト文字列をワイド文字列に変換するために使用されます。そのプロトタイプはヘッダー ファイル stdlib.h で定義されます。

size_t mbstowcs(
  wchar_t* wcstr、
  const char* mbstr、
  size_t カウント
);

最初のパラメータ wcstr はターゲットのワイド文字列、2 番目のパラメータ mbstr は変換されるマルチバイト文字列、3 番目のパラメータは変換されるマルチバイト文字列の最大数です。文字。

変換が成功した場合、戻り値は変換に成功したマルチバイト文字の数です。変換が失敗した場合は、「-1」が返されます。戻り値が 3 番目の引数と同じである場合、変換されたワイド文字列は NULL で終了しません。

以下に例を示します。

setlocale(LC_ALL, "");

char* mbs = "天気は良いです";
wchar_t wcs[20];

int nBytes = 0;
nBytes = mbstowcs(wcs, mbs, 20);

printf("%ls\n", wcs); // 天気は良いです
printf("%d\n", nBytes);

上記の例では、マルチバイト文字列 mbsmbstowcs() によってワイド文字列に変換され、4 文字が正常に変換されたため、この関数の戻り値は 4 になります。

mbstowcs() の最初のパラメータが NULL の場合、ターゲットのワイド文字列に含まれる文字数を返します。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0