Как преобразовать кодовую точку в utf-8?

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

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

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

?

Вы хотите обрабатывать любые кодовые точки (до 01FFFFF) или только кодовые точки Basic Multiligual Planance (до 0xFFFF)? Если в первом случае вы будете использовать char16_t для utf8, во втором случае char32_t для utf8...

Serge Ballesta 28.05.2019 13:34

@SergeBallesta Я еще не решил этого. Неподписанный int был получен через std::strtol

darune 28.05.2019 13:44

Так что, может быть, просто 0xFFFF - мне было бы интересно полное решение ... так что оба решения могут работать

darune 28.05.2019 13:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
849
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете сделать это со стандартной библиотекой, используя 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. В стандартной библиотеке нет альтернатив, и в настоящее время рекомендуется использовать специальную библиотеку.

eerorika 28.05.2019 14:00

Но да, ключ в том, что у вас [случайно] есть UTF-32!

Lightness Races in Orbit 28.05.2019 14:18

Хм, я не могу использовать эту опцию, так как она устарела в С++ 17. У вас есть нерекомендуемый обходной путь? :)

darune 28.05.2019 14:27
Ответ принят как подходящий

Как уже упоминалось, кодовая точка в этой форме (удобно) 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 28.05.2019 21:05

@darune Звучит разумно. Кстати, это "предостережение", одно слово :)

Lightness Races in Orbit 29.05.2019 11:32

В 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)

"char будет заменен на char8_t" Заявление, которое может ввести в заблуждение?
Lightness Races in Orbit 28.05.2019 16:57

Вы написали char16_t не char32_t в своей демонстрации; преднамеренный?

Lightness Races in Orbit 28.05.2019 16:59

@LightnessRacesinOrbit: Да, если прочитать это после вашего комментария, это может ввести в заблуждение. Я отредактировал его (и исправил глупые 16...). Спасибо, что заметили.

Serge Ballesta 28.05.2019 17:05

@LightnessRacesinOrbit: Эээ... Английский не мой родной язык, и я не смог понять ваш последний комментарий. Я предполагаю, что Наааис для хорошего, но что такое нп?

Serge Ballesta 28.05.2019 17:21

В Интернете говорят «нет проблем».

Lightness Races in Orbit 28.05.2019 17:26

Прочитав о нестабильном состоянии поддержки 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.

Другие вопросы по теме