如何将代码点转换为 utf-8

How to convert a codepoint to utf-8?

本文关键字:转换 utf-8 代码      更新时间:2023-10-16

我有一些代码在 unicode 代码点中读取(如字符串 0xF00 中的转义(。

由于我使用 boost,我推测以下是否是最佳(和正确(的方法:

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

您可以使用标准库执行此操作,使用 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);
}

这将返回大小为 1、2、3 或 4 的std::string,具体取决于codepoint的大小。如果代码点太大(> 0x10FFFF,最大 unicode 代码点(,它将抛出std::range_error


您的 boost 版本似乎也在做同样的事情。文档说utf_to_utf函数将一个 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);
}

如前所述,这种形式的代码点是(方便的(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(。

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> .

以下是在 utf8 中转换代码点(最多 0x10FFFF(的一些代码:

// 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(

在阅读了 C++ 中 UTF-8 支持的不稳定状态后,我偶然发现了相应的 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的来回调用。