Posted at

C/C++ におけるプラットフォームごとの文字変換処理

More than 3 years have passed since last update.

ここではクロスプラットフォームに利用できる ICU と OS 固有の関数 (Windows/OSX) の3つの例を紹介する。クロスプラットフォームに利用できる候補として libiconv (iconv) もあるが今回取り上げない。出来れば ICU を使うべき。


ICU

ucnv_toUChars/ucnv_fromUChars を使う。

/* ucnv_toUChars の例。エラー処理を端折ってる点に注意 */

UConverter * converter = ucnv_open("shift_jis", &status);
const char *ms = "test";
size_t length = strlen(ms);
UErrorCode code = U_ZERO_ERROR;
size_t capacity = length * ucnv_getMinCharSize(converter) + 1;
UChar *us = (UChar *) calloc(capacity, sizeof(UChar));
int written = ucnv_toUChars(converter, us, capacity, ms, length, &code);
ucnv_close(converter);

/* ucnv_fromUChars の例。エラー処理を端折ってる点に注意 */
UConverter * converter = ucnv_open("shift_jis", &status);
const UChar us[] = { 't', 'e', 's', 't', 0 } ;
size_t length = u_strlen(us);
UErrorCode code = U_ZERO_ERROR;
size_t capacity = length * ucnv_getMaxCharSize(converter) + 1;
char *ms = (char *) calloc(capacity, sizeof(char));
int written = ucnv_fromUChars(converter, ms, capacity, us, length, &code);
ucnv_close(converter);

pre-flighting 形式も使用できるが、ここでは事前に必要なサイズを ucnv_get(Min|Max)CharSize で計算してから変換する処理にした。


Windows

Windows API の一部として入ってる MultiByteToWideChar/WideCharToMultiByte を使う。

/* MultiByteToWideChar の例。エラー処理を端折ってる点に注意 */

const char *ms = "test";
size_t length = strlen(ms);
UINT codepage = CP_UTF8;
/* pre-flighting 形式で事前に変換される長さを求める */
int capacity = MultiByteToWideChar(codepage, 0 /* MB_PRECOMPOSED */, ms, length, NULL, 0);
wchar_t *ws = (wchar_t *) calloc(capacity, sizeof(wchar_t));
/* マルチバイト文字からワイド文字に変換 */
int written = MultiByteToWideChar(codepage, 0, ms, length, ws, capacity);

/* WideCharToMultiByte の例。エラー処理を端折ってる点に注意 */
const wchar_t *ws= L"test";
size_t length = wcslen(ws);
UINT codepage = CP_UTF8;
/*
* pre-flighting 形式で事前に変換される長さを求める
* 後ろ2つの引数は変換失敗時の処理だがここではシステム規定とする
*/

int capacity = WideCharToMultiByte(codepage, 0 /* WC_SEPCHARS */, ws, length, NULL, 0, NULL, NULL);
char *ms = (char *) calloc(capacity + 1, sizeof(char));
/* ワイド文字からマルチバイト文字に変換 */
int written = WideCharToMultiByte(codepage, 0, ws, length, ms, capacity, NULL, NULL);

最初の引数は CP_ ではじまる定数以外 Code Page Identifiers から使用したい文字コードからコードページを指定する(例えば Shift_JIS の場合 932)。例外は UTF-16 で wchar_t が元々 UTF-16 形式で格納されているため。


OSX

CFString または NSString を使うが、ここでは CFString を使う。

/* CFStringRef に変換する。エラー処理を端折ってる点に注意 */

const char *ms = "test";
size_t length = strlen(ms);
CFStringEncoding encoding = kCFStringEncodingMacJapanese;
CFStringRef s = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *) ms, length, encoding, FALSE);

/* CFStringRef からバイト文字を取得する形で文字変換する。エラー処理を端折ってる点に注意 */
CFStringRef s = CFSTR("test");
CFIndex length = CFStringGetLength(us);
CFStringEncoding encoding = kCFStringEncodingMacJapanese;
size_t capacity = CFStringGetMaximumSizeForEncoding(length, encoding);
UInt8 *ms = (UInt8 *) calloc(capacity, sizeof(UInt8));
CFIndex written;
CFStringGetBytes(s, CFRangeMake(0, length), encoding, '?', FALSE, ms, capacity, &written);

ShiftJIS 関連の定数が定義されており CFStringGetListOfAvailableEncodings を呼び出しても利用可能として入っている (on OSX 10.9) が、どうも kCFStringEncodingMacJapanese しか機能しないのは何か処理が必要なのだろうか?


CFString

 - https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFStrings/introCFStrings.html

 - https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFStringRef/Reference/reference.html


NSString