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

时间:2019-05-28 11:29:28

标签: c++ boost utf-8 c++17 boost-locale

我有一些代码可以读取unicode代码点(以0xF00字符串形式转义)。

自从我使用以来,我一直在猜测以下方法是否是最佳(也是正确的)方法:

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

4 个答案:

答案 0 :(得分:4)

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

(并考虑从C ++ 20开始读入vector<char8_t>std::u8string)。

(最后,请注意,我专门使用uint32_t来确保输入的宽度正确。)

我倾向于在项目中使用该库,直到我需要一些其他用途的东西为止(此时,我通常会切换到ICU)。

答案 1 :(得分:3)

您可以使用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的大小。如果代码点太大(大于0x10FFFF,即最大unicode代码点),它将抛出std::range_error


您的带有Boost的版本似乎在做同样的事情。 The documentation表示utf_to_utf函数将UTF编码转换为另一种,在这种情况下为32到8。如果您使用char32_t,这将是一种“正确”的方法,它将起作用在unsigned intchar32_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 :(得分:2)

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

答案 3 :(得分:1)

在阅读了有关C ++中UTF-8支持的不稳定状态后,我偶然发现了相应的C支持c32rtomb,它看起来很有希望,而且很可能很快就会被弃用

*if(rs.next())
            {
                rs.last();
                totalNumberOfRecords=rs.getRow();               
                rs.beforeFirst();
            }*

用法将是

#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;
}

如果应用程序的当前语言环境已经是UTF-8,则可以省略对char32_t codepoint{0xfff}; char buf[MB_LEN_MAX]{}; size_t len = to_utf8(codepoint, buf); 的来回调用。

相关问题