У меня есть код, который читается в кодовой точке Unicode (как экранированная строка 0xF00).
Поскольку я использую увеличение, я предполагаю, что следующий подход является лучшим (и правильным):
unsigned int codepoint{0xF00};
boost::locale::conv::utf_to_utf<char>(&codepoint, &codepoint+1);
?
@SergeBallesta Я еще не решил этого. Неподписанный int был получен через std::strtol
Так что, может быть, просто 0xFFFF - мне было бы интересно полное решение ... так что оба решения могут работать





Вы можете сделать это со стандартной библиотекой, используя 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);
}
Напоминаем, что wstring_convert и codecvt_utf8 устарели, начиная с C++17. В стандартной библиотеке нет альтернатив, и в настоящее время рекомендуется использовать специальную библиотеку.
Но да, ключ в том, что у вас [случайно] есть UTF-32!
Хм, я не могу использовать эту опцию, так как она устарела в С++ 17. У вас есть нерекомендуемый обходной путь? :)
Как уже упоминалось, кодовая точка в этой форме (удобно) UTF-32, поэтому вам нужно перекодирование.
Для решения, которое не полагается на функции, устаревшие с C++17, и не является действительно уродливым, а также не требует здоровенных сторонних библиотек, вы можете использовать очень легкий UTF8-CPP (четыре маленьких заголовка!) и его функция 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).
Это отличное решение (с использованием небольшой библиотеки только для заголовков) и пока что лучшее, на мой взгляд, и я, вероятно, мог бы его использовать. Однако, поскольку у меня уже есть повышение - я не вижу причин менять его с учетом текущих потребностей - и учитывая, что он будет делать то, что я ожидать, и нет никаких проблем. Одной из причин, по которой я спросил, было также то, что мне не хватает документации по бустингу, или я просто не смог найти ее. Я надеюсь, что это имеет смысл.
@darune Звучит разумно. Кстати, это "предостережение", одно слово :)
В 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,
buf, buf+sz, last_out);
sz = last_out - buf;
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)
Вы написали char16_t не char32_t в своей демонстрации; преднамеренный?
@LightnessRacesinOrbit: Да, если прочитать это после вашего комментария, это может ввести в заблуждение. Я отредактировал его (и исправил глупые 16...). Спасибо, что заметили.
@LightnessRacesinOrbit: Эээ... Английский не мой родной язык, и я не смог понять ваш последний комментарий. Я предполагаю, что Наааис для хорошего, но что такое нп?
В Интернете говорят «нет проблем».
Прочитав о нестабильном состоянии поддержки UTF-8 в C++, я наткнулся на соответствующую поддержку C c32rtomb, которая выглядит многообещающе и, вероятно, не будет объявлена устаревшей в ближайшее время.
#include <clocale>
#include <cuchar>
#include <climits>
size_t to_utf8(char32_t codepoint, char *buf)
{
const char *loc = std::setlocale(LC_ALL, "en_US.utf8");
std::mbstate_t state{};
std::size_t len = std::c32rtomb(buf, codepoint, &state);
std::setlocale(LC_ALL, loc);
return len;
}
Тогда использование будет
char32_t codepoint{0xfff};
char buf[MB_LEN_MAX]{};
size_t len = to_utf8(codepoint, buf);
Если текущим языковым стандартом вашего приложения уже является UTF-8, вы, конечно, можете опустить обратный вызов setlocale.
Вы хотите обрабатывать любые кодовые точки (до 01FFFFF) или только кодовые точки Basic Multiligual Planance (до 0xFFFF)? Если в первом случае вы будете использовать char16_t для utf8, во втором случае char32_t для utf8...