Linux環境でUTF-8エンコーディングを使用していると仮定すると、次のコードは、C++でのUnicode処理を容易にするためにプログラムを準備します。
int main(int argc, char * argv[]) {
std::setlocale(LC_CTYPE, "");
// ...
}
次に、Linuxではwchar_t型は32ビットです。つまり、個々のUnicodeコードポイントを保持でき、C ++での従来の文字列処理(文字ごと)にwstring型を安全に使用できます。上記のsetlocale呼び出しでは、wcoutに挿入すると出力がUTF-8に自動的に変換され、wcinから抽出するとUTF-8入力がUTF-32に自動的に変換されます(1文字= 1コードポイント)。残っている唯一の問題は、argv[i]文字列がまだUTF-8でエンコードされていることです。
次の関数を使用して、UTF-8をUTF-32にデコードできます。入力文字列が破損している場合、UTF-8ルールが破られた場所まで、適切に変換された文字が返されます。より多くのエラー報告が必要な場合は、それを改善できます。しかし、argvデータの場合、それが正しいUTF-8であると安全に想定できます。
#define ARR_LEN(x) (sizeof(x)/sizeof(x[0]))
wstring Convert(const char * s) {
typedef unsigned char byte;
struct Level {
byte Head, Data, Null;
Level(byte h, byte d) {
Head = h; // the head shifted to the right
Data = d; // number of data bits
Null = h << d; // encoded byte with zero data bits
}
bool encoded(byte b) { return b>>Data == Head; }
}; // struct Level
Level lev[] = {
Level(2, 6),
Level(6, 5),
Level(14, 4),
Level(30, 3),
Level(62, 2),
Level(126, 1)
};
wchar_t wc = 0;
const char * p = s;
wstring result;
while (*p != 0) {
byte b = *p++;
if (b>>7 == 0) { // deal with ASCII
wc = b;
result.push_back(wc);
continue;
} // ASCII
bool found = false;
for (int i = 1; i < ARR_LEN(lev); ++i) {
if (lev[i].encoded(b)) {
wc = b ^ lev[i].Null; // remove the head
wc <<= lev[0].Data * i;
for (int j = i; j > 0; --j) { // trailing bytes
if (*p == 0) return result; // unexpected
b = *p++;
if (!lev[0].encoded(b)) // encoding corrupted
return result;
wchar_t tmp = b ^ lev[0].Null;
wc |= tmp << lev[0].Data*(j-1);
} // trailing bytes
result.push_back(wc);
found = true;
break;
} // lev[i]
} // for lev
if (!found) return result; // encoding incorrect
} // while
return result;
} // wstring Convert