Recardo_Recoly,
Понятно.
1. Лучше использовать WM_UNICHAR, он работает и с кодами более 65535. Поддерживается как минимум WinXP.
2. Как превратить кодовую позицию в UTF-8, есть много вариантов. Сейчас найду свой.
enum {
SURROGATE_MIN = 0xD800,
SURROGATE_MAX = 0xDFFF,
SURROGATE_LO_MIN = SURROGATE_MIN,
SURROGATE_HI_MIN = 0xDC00,
SURROGATE_LO_MAX = SURROGATE_HI_MIN - 1,
SURROGATE_HI_MAX = SURROGATE_MAX,
UNICODE_MAX = 0x10FFFF,
U8_1BYTE_MAX = 0x7F,
U8_2BYTE_MIN = 0x80,
U8_2BYTE_MAX = 0x7FF,
U8_3BYTE_MIN = 0x800,
U8_3BYTE_MAX = 0xFFFF,
U8_4BYTE_MIN = 0x10000,
U8_4BYTE_MAX = UNICODE_MAX,
U16_1WORD_MAX = 0xFFFF,
U16_2WORD_MIN = 0x10000,
U16_2WORD_MAX = UNICODE_MAX,
};
void str::putCpNe (char*& p, unsigned long aCp)
{
if (aCp <= U8_2BYTE_MAX) { // 1 or 2 bytes, the most frequent case
if (aCp <= U8_1BYTE_MAX) { // 1 byte
*(p++) = static_cast<char>(aCp);
} else { // 2 bytes
*(p++) = static_cast<char>((aCp >> 6) | 0xC0);
*(p++) = static_cast<char>((aCp & 0x3F) | 0x80);
}
} else { // 3 or 4 bytes
if (aCp <= U8_3BYTE_MAX) { // 3 bytes
*(p++) = static_cast<char>( (aCp >> 12) | 0xE0);
*(p++) = static_cast<char>(((aCp >> 6) & 0x3F) | 0x80);
*(p++) = static_cast<char>( (aCp & 0x3F) | 0x80);
} else { // 4 bytes
*(p++) = static_cast<char>(((aCp >> 18) & 0x07) | 0xF0);
*(p++) = static_cast<char>(((aCp >> 12) & 0x3F) | 0x80);
*(p++) = static_cast<char>(((aCp >> 6) & 0x3F) | 0x80);
*(p++) = static_cast<char>( (aCp & 0x3F) | 0x80);
}
}
}
void str::appendCp(std::string & s, unsigned long aCp)
{
char c[5];
char* end = c;
putCpNe(end, aCp);
s.append(c, end);
}
Слово Cp у меня означает code point, кодовая позиция Юникода. Ne — no error-checking, без проверки кодовых позиций на корректность.
В Си++ есть и штатные функции преобразования кодовых позиций Юникода в UTF-8 и UTF-16. Но страшны, как чёрт, и половина из них в C++17 deprecated :(
И последнее, что я хочу сказать.
Вы путаете две вещи: Юникод и его кодовые позиции, и форматы записи юникодных строк UTF-8 и UTF-16. Соотношение «один символ Юникода — один char» только в UTF-32!