Как преобразовать кодовую точку в utf-8? - PullRequest
3 голосов
/ 28 мая 2019

У меня есть некоторый код, который читает в кодовой точке Unicode (как экранированный в строке 0xF00).

Поскольку я использую , я полагаю, что следующее лучше (и правильно) подход:

unsigned int codepoint{0xF00};
boost::locale::conv::utf_to_utf<char>(&codepoint, &codepoint+1);

?

Ответы [ 3 ]

4 голосов
/ 28 мая 2019

Как уже упоминалось, кодовой точкой в ​​этой форме (удобно) является UTF-32, поэтому то, что вы ищете, - это транскодирование.

Для решения, которое не полагается на функции, устаревшие с C ++17, и не очень уродливо, а также не требует здоровенных сторонних библиотек, вы можете использовать очень легкий UTF8-CPP (четыре маленьких заголовка!) И егоfunction utf8::utf32to8.

Это будет выглядеть примерно так:

const uint32_t codepoint{0xF00};
std::vector<unsigned char> result;

try
{
   utf8::utf32to8(&codepoint, &codepoint + 1, std::back_inserter(result));
}
catch (const utf8::invalid_code_point&)
{
   // something
}

(Там также utf8::unchecked::utf32to8, если у вас аллергия на исключения.)

(И подумайте о чтении в vector<char8_t> или std::u8string, начиная с C ++ 20).

(Наконец, обратите внимание, что я специально использовал uint32_t, чтобы обеспечить правильную ширину ввода.)

Я склонен использовать эту библиотеку в проектах, пока мне не понадобится что-то более тяжелое для других целей (с этого момента я обычно переключусь на ICU).

3 голосов
/ 28 мая 2019

Вы можете сделать это с помощью стандартной библиотеки, используя std::wstring_convert для преобразования UTF-32 (кодовые точки) в UTF-8:

#include <locale>
#include <codecvt>

std::string codepoint_to_utf8(char32_t codepoint) {
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
    return convert.to_bytes(&codepoint, &codepoint + 1);
}

Возвращает std::stringразмер которого составляет 1, 2, 3 или 4 в зависимости от размера codepoint.Он выдаст std::range_error, если кодовая точка слишком велика (> 0x10FFFF, максимальная кодовая точка Unicode).


Ваша версия с boost, похоже, делает то же самое. В документации говорится, что функция utf_to_utf преобразует кодировку UTF в другую, в данном случае с 32 на 8. Если вы используете char32_t, это будет "правильный" подход, который будет работать насистемы, в которых unsigned int отличается от размера char32_t.

// The function also converts the unsigned int to char32_t
std::string codepoint_to_utf8(char32_t codepoint) {
    return boost::locale::conv::utf_to_utf<char>(&codepoint, &codepoint + 1);
}
2 голосов
/ 28 мая 2019

C ++ 17 устарел, число удобных функций обработки utf. К сожалению, последние оставшиеся будут устаревшими в C ++ 20 (*) . Это сказанное std::codecvt все еще в силе. От C ++ 11 до C ++ 17 вы можете использовать std::codecvt<char32_t, char, mbstate_t>, начиная с C ++ 20 это будет std::codecvt<char32_t, char8_t, mbstate_t>.

Вот код, преобразующий кодовую точку (до 0x10FFFF) в utf8:

// codepoint is the codepoint to convert
// buff is a char array of size sz (should be at least 4 to convert any code point)
// on return sz is the used size of buf for the utf8 converted string
// the return value is the return value of std::codecvt::out (0 for ok)
std::codecvt_base::result to_utf8(char32_t codepoint, char *buf, size_t& sz) {
    std::locale loc("");
    const std::codecvt<char32_t, char, std::mbstate_t> &cvt =
                   std::use_facet<std::codecvt<char32_t, char, std::mbstate_t>>(loc);

    std::mbstate_t state{{0}};

    const char32_t * last_in;
    char *last_out;
    std::codecvt_base::result res = cvt.out(state, &codepoint, 1+&codepoint, last_in,
                                            c, c+sz, last_out);
    sz = last_out - c;
    return res;
}

(*) std::codecvt все еще будет существовать в C ++ 20. Просто экземпляры по умолчанию больше не будут std::codecvt<char16_t, char, std::mbstate_t> и std::codecvt<char32_t, char, std::mbstate_t>, а std::codecvt<char16_t, char8_t, std::mbstate_t> и std::codecvt<char32_t, char8_t, std::mbstate_t> (примечание char8_t вместо char)

...