
char 型で格納できるサイズは ASCII コードの 1 文字分、8ビットであり、 これでは日本語の文字数をカウントしたり、検索することが上手く来ません。
C言語で日本語に対して文字列操作を行う場合は、ワイド文字かマルチバイト文字を 扱う必要があります。
今までのプログラムの中でも char 型の配列を使って日本語を表示してきました。
printf("薬草A");
みたいな感じですね。
この文字列 "テストabc" は日本語 Windows 環境(Shift-JIS)であれば、 "薬"、"草" はそれぞれ 2 バイト、"A" は 1 バイトを使って表現されます。
2バイトの文字や 1 バイトの文字が混在している状態なので、マルチバイト(または複バイト)と表現します。
一方、ワイド文字は、1文字あたり 2 バイトで入る大きさのメモリを固定で用意しておきます。 "薬"、"草"、"A" 、すべて 2 バイトの場所に入っているイメージです。 ただ、C言語でワイド文字は 1 文字あたり 2 バイトと定義されているとは限りません。
1文字当たり 2 バイトだったり、 4 バイトであったり、環境によって使用するメモリサイズが異なります。
私の使っている Windows10 + Cygwin + gcc 環境では、ワイド文字は 2 バイトでした。
古くからある Shift-JIS と呼ばれる日本語のコード表示では 1 文字 2 バイトですし、 UTF-16 については一部の例外となる文字(サロゲートペア文字とよばれます)があるものの、 通常 1 文字は 2 バイト以内で表現できるので、ワイド文字列を使って文字列を処理すれば十分、日本語の処理ができそうです。
サロゲートペア 中国語の「こんにちは」の意味である、ニーハオ(你好)の "你" のように UTF-16 の 2 バイトで表現できない文字もあります。こういった文字は、2文字ぶんのサイズ(UTF-16 では 4バイト)を使用します。このような文字をサロゲートペアと呼びます。他にも日本語の旧字体はサロゲートペアとなったりします。
#include <stdio.h>
#include <string.h>
int main(){
char *name = "森A";
printf("「%s」の文字数は %d !?\n", name, strlen(name));
}
実行結果は下の用になりました。
「森A」の文字数は 4 !?
文字の長さを確認する関数 strlen を、マルチバイトの文字列 "森A" に使ってみましたが、 2 ではなく、 4 という結果が返ってきました。
文字の長さを取得する strlen 関数は、ワイド文字に対応していないため、 うまくカウントできていないことがわかります。
こういった日本語など、ASCIIコード以外が含まれる文字列を扱うには、 マルチバイト用の関数、もしくはワイド文字列用の関数を使って処理する必要があります。
ワイド文字を扱いたい場合、ワイド文字を処理する専用の関数が用意されています。
wchar.h をインクルードすることでワイド文字を操作する関数を利用できます。
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main(){
setlocale(LC_ALL, "");
wchar_t message[] = L"薬草abcを合成";
int count = wcslen(message);
wprintf( L"ワイド文字列=「%ls」文字の数は %d 文字。\n" , message , count );}
実行結果
ワイド文字列=「薬草abcを合成」文字の数は 8 文字。
コードを見ていきましょう。
今回、3行目で wchar.h のほか、 locale.h をインクルードしています。 ヘッダーファイル「locale.h」にはプログラムを国際化する機能の定義が入っています。
通常の C 言語プログラムで、アメリカ英語のみを使った動作をするのであれば、 国際化は不要ですが、日本語などの英語以外を利用する場合に必要となる場合があります。
5 行目で setlocale 関数が登場しています。
setlocale( LC_ALL, "");
ここで、関数に使われている単語 "locale" とは「言語、国、地域設定」の意味です。 setlocale 関数を呼び出すことで、システムで設定されているネイティブロケール(そのPCの言語設定)を プログラム内に引き渡して設定することができます。 以降、ワイド文字に対応した関数は、ここで定義されたロケールに従って、日本語など、英語以外の言語を処理できます。
C言語ではプログラムの最初に一度呼び出してあげれば大丈夫です。
6行目で、wchar_t 型の配列変数 name を宣言&初期化しています。
使い方は char 型と一緒です。
wchar_t message[] = L"薬草abcを合成";
wchar_t message[] にはワイド文字列を代入できます。 ワイド文字列は L"文字列" のように、文字列(文字リテラル)の先頭に Lを付けています。
これで、コンパイラが自動的に、ワイド文字列として処理してくれます。 私の環境では、ワイド文字は 1 文字 2 バイトで固定ですので、 L"薬草abcを合成" に含まれる '薬' も 'a' も全て 1 文字あたり 2 バイト使って変数 message に格納されます。
続いて 7 行目です。
int count = wcslen(message);
新しく wcslen 関数が登場しています。 この関数はワイド文字列の文字数をカウントした結果を返してくれます。
通常の char 配列であれば、strlen 関数を使って長さを確認していましたが、ワイド文字列には wcslen 関数を使って長さを確認しています。
続いて 8 行目です。
wprintf( L"ワイド文字列=「%ls」文字の数は %d 文字。\n" , message , count );
wprintf 関数が登場しています。使い方は printf 関数と同じで、違うのは、ワイド文字をそのまま表示できるということです。
識別子「%ls」はワイド文字の文字列を表示する識別子です。
第二引数にはワイド文字列が入っている変数 message を指定し、 %ls の部分に埋め込まれて表示されます。
char 型配列のマルチバイト文字列から wchar_t 型配列の文字列(ワイド文字列)に 変換するためには、mbrtowc 関数を使用します。
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>
int main(){
setlocale(LC_ALL, "");
char orig[] = "薬草B";
wchar_t henkan[256];
int len = mbstowcs ( henkan, orig , sizeof(orig) );
wprintf( L"ワイド文字列=「%ls」で、文字数は %d です。\n" , henkan , len);
}
下のような実行結果になります。
ワイド文字列=「薬草B」で、文字数は 3 です。
ちゃんと char 型配列の文字列 "薬草B" が、ワイド文字列に変換されて wprintf 関数で表示できています。
mbstowcs 関数は下のような定義になっています。
定義:
size_t mbstowcs(wchar_t * convertedWideChar, const char * source, size_t length);
実際に使う時は下のように呼び出しできます。
int 変換したワイド文字列 = mbstowcs( wchar_t *変換結果を入れる先, char *変換元, 変換するバイト数 );
今回は 9 行目で mbstrowcs 関数を呼び出して下の用に変換をかけています。
int len = mbstowcs ( henkan, orig , sizeof(orig) );
char 型配列の orig をワイド文字列に変換し、wchar_t型配列の henakn に結果を入れています。 変換する文字列の長さは、sizeof (orig) と指定しており、変数 orig のサイズそのもの、つまり、orig 変数に入っている 文字列すべてを変換しています。
Windows環境のコンソールでは、通常 Shift-JIS コード(正確には MS932 と呼ばれる Microsoft社の Shift-JIS独自拡張)が利用されています。
1 文字 2 バイトで固定のShift-JIS規格や UTF-16 規格ではなく、使う文字によって必要なバイト数が異なる、UTF-8 規格の文字を扱うなど、マルチバイトを直接取り扱うことも可能です。
C11仕様からは、uchar.h というヘッダーをインクルードすることで、Unicode の UTF-8 など、マルチバイトに対応した文字を扱いやすくなります。
ただし、C言語の標準ライブラリでこれらを使ったマルチバイト文字列を操作する関数が十分でないので、 このシリーズの中では以降、ワイド文字を使って日本語の操作を行います。
例えば Windows 環境でプログラミングをしていると、TEXT 型、LPSTR 型等の環境独自の文字列型が登場してきます。
必要に応じて、どんな特徴を持った型なのか調べていくのが良いです!
コメント、ありがとうございます。
ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。
Twitter:@NodachiSoft_jpお名前:以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください
お名前: