char 型でヌル終端の文字列でかつ不正なバイト列が含まれておらず、かつロケールを決め打ちにしてよいのであれば、1文字ずつ取り出すのに mblen
、mbrlen
、mbtowc
、mbrtowc
、mbrtoc32
を使うことができます。これらの関数の戻り値は次の文字のバイト数です。
mblen
と mbrlen
の使いわけは状態変数を自分で管理する必要があるかどうかです。mbrlen
の第3引数に内部の状態をあらわす変数を指定します。
mbtowc
と mblen
の違いはコードポイントが必要であるかどうかです。mbtowc
の第1引数にコードポイントが参照渡しされます。第1引数の型は wchar_t なので、OS によって結果が変わります。筆者は wchar_t
のサイズが32ビットである Linux と Mac OS X で確認していますが、16ビットの Windows では未検証です。
mbrtowc
と mbtowc
の使いわけは状態変数を自分で管理する必要があるかどうかです。mbrtowc
の第4引数で内部の状態をあらわす変数を指定します。
mbrtoc32
は C11 で追加された関数で、mbrtowc
と比べて wchar_t
の代わりに char32_t
が使われます。2015年1月の時点では GCC で利用可能です。
mbrtoc16
も同時に C11 で導入されましたが、U+10000 から U+10FFFF の範囲の文字に遭遇すると負の値を返すので、使い道はかぎられます。
複数の種類の OS で利用することを想定するのであれば ICU を検討する必要があるでしょう。こちらの記事をご参照ください。
テストケース
次の文字列を試すことにします。「?」(U+20BB7) は基本多言語面外の文字 (U+10000 から U+10FFFF) に対応しているかどうかを確かめるために使います。
#include <stdio.h>
#include <locale.h>
#include <stdlib.h>
void print_each_char(const char*);
int main(void)
{
//const char* str = "\U00020BB7\u91CE\u5BB6";
const char* str = "?野家";
const char* str2 = "?野\x80家";
setlocale(LC_CTYPE, "ja_JP.UTF-8");
print_each_char(str);
puts("");
print_each_char(str2);
return 0;
}
mblen
void print_each_char(const char* str)
{
char size;
mblen(NULL, 0);
while (*str) {
size = mblen(str, MB_CUR_MAX);
if (size < 1) {
puts("不正なバイト列に遭遇したので、処理を中断しました。");
break;
}
printf("%.*s\n", size, str);
str += size;
}
}
mbrlen
mbstate_t
を使うために wchar.h
のインクルードが必要になります。
#include <wchar.h>
void print_each_char(const char* str)
{
char size;
mbstate_t state;
while (*str) {
size = mbrlen(str, MB_CUR_MAX, &state);
if (size < 1) {
puts("不正なバイト列に遭遇したので、処理を中断しました。");
break;
}
printf("%.*s\n", size, str);
str += size;
}
}
mbtowc
#include <wchar.h>
void print_each_char(const char* str)
{
char size;
wchar_t cp;
mbtowc(&cp, NULL, 0);
while (*str) {
size = mbtowc(&cp, str, MB_CUR_MAX);
if (size < 1) {
puts("不正なバイト列に遭遇したので、処理を中断しました。");
break;
}
printf("U+%X %.*s\n", cp, size, str);
str += size;
}
}
mbrtowc
#include <wchar.h>
void print_each_char(const char* str)
{
char size;
wchar_t cp;
mbstate_t status;
mbsinit(&status);
while (*str) {
size = mbrtowc(&cp, str, MB_CUR_MAX, &status);
if (size < 1) {
puts("不正なバイト列に遭遇したので、処理を中断しました。");
break;
}
printf("U+%X %.*s\n", cp, size, str);
str += size;
}
}
mbrtoc32
C11 で導入された mbrtoc32
は GCC で利用可能である。
#include <wchar.h>
#include <uchar.h>
void print_each_char(const char* str)
{
char size;
char32_t cp;
mbstate_t status;
mbsinit(&status);
while (*str) {
size = mbrtoc32(&cp, str, MB_CUR_MAX, &status);
if (size < 1) {
puts("不正なバイト列に遭遇したので、処理を中断しました。");
break;
}
printf("U+%X %.*s\n", cp, size, str);
str += size;
}
}
文字数を求める
文字数を求めるだけであれば mbstowcs
を使うことができる。ただし mbstowcs
は OS ごとの wchar_t
の違いを考慮しなければならない。
mbstowcs
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <stdlib.h>
int main(void)
{
const char* str = "?野家";
size_t size = strlen(str);
wchar_t wchar[1000];
setlocale(LC_CTYPE, "ja_JP.UTF-8");
printf("%zu\n", mbstowcs(wchar, str, size));
return 0;
}