マルチバイト文字列とワイド文字列の相互変換サンプルコード

Clang はソースコードを UTF-8 で記述する必要があるため、exeClang がサポートする char 型の文字列リテラル( const char *s = "..."; )のエンコーディングは UTF-8 のみになります。exeGCC も Clang は同様です。GCC の方は -finput-charset でソースコードのエンコーディングを、 -fexec-charset で文字列リテラルのエンコーディングを変更できるので、文字列リテラルは cp932 と UTF-8 の 2 つのエンコーディングをサポートしていますが、ここでは UTF-8 のみを扱います。

注釈

Clang はソースコードに直接 cp932 の文字列リテラルを記述することはできませんが、ファイルなどから cp932 エンコーディングの文字列データを読み込み、 setlocale(LC_CTYPE, "cp932") に設定すれば exeGCC と同様に動くと思われますが、テストもサポートも行っておりません。

注釈

cp932 は Shift_JIS と同じエンコーディング方式ですが、Microsoft 社独自の文字が追加されています。BSD 界隈では MSKanji とも呼ばれます。

また、GCC と Clang 共通の仕様で、wchar_t 型の文字列リテラル( const wchar_t  *s = L"..."; )は UTF32LE(1 文字 4 バイト固定、リトルエンディアン)のワイド文字列になります。(GCC の上記オプションを使用しても変更できません。)

マルチバイト文字列は 1 文字の長さが可変なので(char 型配列としては扱えないので)1 文字ずつ処理を行うことができません。その場合は 1 文字の長さが固定のワイド文字列(wchar_t 型配列として扱える)に変換してから処理を行う必要があります。

以下はマルチバイト文字列をワイド文字列に変換して、文字列の置換処理を行った後、再びマルチバイト文字列に戻して出力するサンプルコードです。サンプルコードは SOLID プロジェクトへの組み込みを想定しているので、主要部分のみを載せています。(以後のサンプルも同様です。)

#include <locale.h> /* setlocale */
#include <stdlib.h> /* mbstowcs, wcstombs */
#include <string.h> /* strlen */
#include <wchar.h> /* wcslen, wcsncpy */

/* 省略 */

const char *utf8str = "この文字列は UTF-8 です。";
int result;
char utf8buf[256];
wchar_t utf32buf[256];

/* 確認のため SOLID-OS の syslog を使用します。 */
syslog_msk_log(LOG_UPTO(LOG_INFO), LOG_UPTO(LOG_INFO));

/* ロケールを utf-8 に設定します。部分文字列も認識します。*/
setlocale(LC_CTYPE, "ja_JP.UTF-8");

/* マルチバイト文字列は文字数と(配列として見た時の)要素数が一致しません。*/
syslog(LOG_INFO, "%s(%d bytes)", utf8str, strlen(utf8str));

/* マルチバイト文字列(UTF-8)からワイド文字(UTF32LE)配列への変換。 */
result = mbstowcs(utf32buf, utf8str, 256);

if (result < 0) {
    syslog(LOG_INFO, "FAIL: mbstowcs()");
}

/* 何か処理を行う */
for (int i = 0; i < wcslen(utf32buf); i++) {
    if (utf32buf[i] == L'8') {
        /* "この文字列は UTF-32 です。" に書き換える。*/
        wcsncpy(&utf32buf[i], L"32 です。", 256);
        break;
    }
}

/* ワイド文字列(UTF32LE)からマルチバイト文字列(UTF-8)への変換。 */
result = wcstombs(utf8buf, utf32buf, 256);

if (result < 0) {
    syslog(LOG_INFO, "FAIL: wcstombs()");
} else {
    syslog(LOG_INFO, "%s", utf8buf);
}

/* ワイド文字列は文字数と要素数が一致します。*/
syslog(LOG_INFO, "(%d chars)", wcslen(utf32buf));

以下は TeraTerm (UTF-8) への出力例となります。

この文字列は UTF-8 です。(34 bytes)
この文字列は UTF-32 です。
(17 chars)

ワイド文字列からマルチバイト文字列への変換

以下はワイド文字列をマルチバイト文字列に変換して出力するサンプルコードです。ロケールを設定しなかった場合に失敗することも確認しています。

#include <locale.h> /* setlocale */
#include <stdlib.h> /* wcstombs */

/* ... */

wchar_t wcstr[] = L"この文字列は UCS4 文字集合を UTF32LE エンコーディングしたものとなります。";
int result;
char utf8buf[256];

/* 確認のため SOLID-OS の syslog を使用します。 */
syslog_msk_log(LOG_UPTO(LOG_INFO), LOG_UPTO(LOG_INFO));

/* ワイド文字列(UTF32LE)からマルチバイト文字列(UTF-8)への変換。 */
result = wcstombs(utf8buf, wcstr, 256);

/* デフォルトの "C" ロケールだと ASCII 範囲外の文字を含むため失敗します。*/
if (result < 0) {
    syslog(LOG_INFO, "XFAIL: please setlocale()");
}

/* ロケールを utf-8 に設定します。部分文字列も認識します。*/
syslog(LOG_INFO, "setlocale:%s", setlocale(LC_CTYPE, "ja_JP.UTF-8"));

/* 今度は成功するはず。*/
result = wcstombs(utf8buf, wcstr, 256);

if (result < 0) {
    syslog(LOG_INFO, "FAIL");
} else {
    syslog(LOG_INFO, "SUCC: %s", utf8buf);
}

以下は TeraTerm (UTF-8) への出力例となります。

XFAIL: please setlocale()
setlocale:UTF8
SUCC: この文字列は UCS4 文字集合を UTF-32 LE エンコーディングしたものとなります 。

uchar.h を使用したサンプルコード

C11 から Unicode(マルチバイト文字列と char16_t/char_32_t 型の相互変換)が標準ライブラリでサポートされました(uchar.h)。マルチバイト文字列として exeClang でサポートされるのは UTF-8 のみです。

注釈

exeGCC では uchar.h は未サポートとなります。

#include <locale.h> /* setlocale */
#include <uchar.h>  /* char32_t, c32rtomb */

/* ... */

char32_t c32 = U'あ';
int result;
mbstate_t st = {0};
char utf8buf[MB_CUR_MAX];

/* 確認のため SOLID-OS の syslog を使用します。 */
syslog_msk_log(LOG_UPTO(LOG_INFO), LOG_UPTO(LOG_INFO));

/* ワイド文字(UTF32LE)からマルチバイト文字列(UTF-8)への変換。 */
result = c32rtomb(utf8buf, c32, &st);

/* デフォルトの "C" ロケールだと ASCII 範囲外の文字を含むため失敗します。*/
if (result < 0) {
    syslog(LOG_INFO, "XFAIL: please setlocale()");
}

/* ロケールを utf-8 に設定します。部分文字列も認識します。*/
syslog(LOG_INFO, "setlocale:%s", setlocale(LC_CTYPE, "ja_JP.UTF-8"));

/* 今度は成功するはず。*/
result = c32rtomb(utf8buf, c32, &st);

if (result < 0) {
    syslog(LOG_INFO, "FAIL");
} else {
    syslog(LOG_INFO, "SUCC: %s(%d bytes)", utf8buf, result);
}

以下は TeraTerm (UTF-8) への出力例となります。

XFAIL: please setlocale()
setlocale:UTF-8
SUCC: (3 bytes)