Преобразовать кодовую точку юникода в utf-16

Как в С++ в Windows преобразовать ссылку на символ xml формы &#xhhhh; в строку с прямым порядком байтов utf-16?

Я думаю, что если часть hhhh составляет 4 символа или меньше, то это 2 байта, которые помещаются в один символ utf-16. Но на этой вики-странице есть таблица ссылок на символы, а некоторые в нижней части представляют собой 5-значные шестнадцатеричные символы. числа, которые не помещаются в два байта. Как их можно преобразовать в utf-16?

Мне интересно, если MultiByteToWideChar функция способна выполнять работу.

Мое понимание того, как кодовая точка размером более 2 байтов преобразуется в utf-16, отсутствует! (Или в этом отношении я не слишком уверен, как кодовая точка размером более 1 байта преобразуется в utf-8, но это другой вопрос).

Спасибо.


person Scott Langham    schedule 17.03.2021    source источник
comment
MultiByteToWideChar совершенно не подходит для этой задачи.   -  person Mark Ransom    schedule 17.03.2021
comment
@RemyLebeau, но большая проблема в этом вопросе заключается в том, чтобы в первую очередь преобразовать каждую строку &#xhhhh; в кодовую точку. Как только вы это сделаете, ваш совет может оказаться полезным.   -  person Mark Ransom    schedule 19.03.2021
comment
@MarkRansom тривиально анализировать ссылки на символы XML в числовые значения кодовой точки. Особенно, если вы используете настоящий парсер XML и позволяете ему делать всю работу за вас.   -  person Remy Lebeau    schedule 19.03.2021
comment
@RemyLebeau может быть и так, но забавно, что никто не упомянул об этом раньше. Мне кажется, что это важная часть вопроса.   -  person Mark Ransom    schedule 19.03.2021


Ответы (1)


Кодовые точки Unicode (UTF-32) имеют ширину 4 байта и могут быть преобразованы в символ UTF-16 (и, возможно, суррогат), используя следующий код (который у меня случайно завалялся).

Он не подвергался тщательному тестированию, поэтому с благодарностью принимаются сообщения об ошибках:

/**
 * Converts U-32 code point to UTF-16 (and optional surrogate)
 * @param utf32 - UTF-32 code point
 * @param utf16 - returned UTF-16 character
 * @return - The number code units in the UTF-16 char (1 or 2).
 */
unsigned utf32_to_utf16(char32_t utf32, std::array<char16_t, 2>& utf16)
{
    if(utf32 < 0xD800 || (utf32 > 0xDFFF && utf32 < 0x10000))
    {
        utf16[0] = char16_t(utf32);
        utf16[1] = 0;
        return 1;
    }

    utf32 -= 0x010000;

    utf16[0] = char16_t(((0b1111'1111'1100'0000'0000 & utf32) >> 10) + 0xD800);
    utf16[1] = char16_t(((0b0000'0000'0011'1111'1111 & utf32) >> 00) + 0xDC00);

    return 2;
}
person Galik    schedule 17.03.2021
comment
Вы можете специально обработать диапазон от 0xd800 до 0xdfff, так как это может быть искаженный ввод. - person Mark Ransom; 17.03.2021
comment
@MarkRansom Да, мне было интересно узнать об отсутствии проверки ошибок (я писал это много лет назад). Но, взглянув еще раз на статью в Википедии, там говорится, что, несмотря на то, что диапазон технически плохих кодовых точек, многие программы все равно их допускают ... так что мне придется немного подумать над этим. - person Galik; 17.03.2021
comment
Это также может быть не искаженный ввод, если кодовые точки объединены в пару для создания допустимого символа UTF-16. Например, JSON кодируется таким образом, см., например. Почему JSON кодирует суррогатные пары UTF-16 вместо кодовых точек Unicode напрямую? - person Mark Ransom; 17.03.2021