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

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

# ICU

ucnv_toUChars/ucnv_fromUChars を使う。

```c
/* 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 で計算してから変換する処理にした。

 - http://userguide.icu-project.org/conversion/converters
 - http://icu-project.org/apiref/icu4c/ucnv_8h.html

# Windows

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

```c
/* 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](http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756.aspx) から使用したい文字コードからコードページを指定する（例えば Shift_JIS の場合 932）。例外は UTF-16 で wchar_t が元々 UTF-16 形式で格納されているため。

 - http://msdn.microsoft.com/ja-jp/library/cc448053.aspx (MultiByteToWideChar)
 - http://msdn.microsoft.com/ja-jp/library/cc448089.aspx (WideCharToMultiByte)

# OSX

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

```c
/* 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

 - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/introStrings.html
 - https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html