【问题标题】:How to convert a codepoint to utf-8?如何将代码点转换为 utf-8?
【发布时间】:2019-10-13 22:10:35
【问题描述】:

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

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

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

?

【问题讨论】:

  • 您想处理任何代码点(最多 01FFFFF)还是只处理基本多语言 Plance 代码点(最多 0xFFFF)?如果是前一种情况,您将使用 char16_t 转 utf8,在后一种情况下,您将使用 char32_t 转 utf8...
  • @SergeBallesta 我还没有真正决定。 unsigned int 是通过 std::strtol 检索到的
  • 所以也许只是 0xFFFF - 不过我会对完整的解决方案感兴趣.. 所以这两种解决方案都可以工作

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


【解决方案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);
}

这将返回一个 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);
}

【讨论】:

  • 提醒一下,wstring_convertcodecvt_utf8 自 C++17 起已弃用。标准库中没有替代品,目前的建议是使用专用库。
  • 但是,关键是你 [意外地] 拥有 UTF-32!
  • 嗯,我不能真正使用这个选项,因为它在 c++17 中已被弃用。您是否有未弃用的解决方法? :)
【解决方案2】:

如前所述,这种形式的代码点(很方便)是 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&lt;char8_t&gt;std::u8string,因为 C++20)。

(最后,请注意,我专门使用了uint32_t 来确保输入具有适当的宽度。)

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

【讨论】:

  • 这是一个很好的解决方案(使用一个小型的仅标头库),并且是迄今为止我看到的最好的解决方案——我可能会使用它。然而,由于我已经有了提升——鉴于当前的需求,我认为没有理由改变——并且鉴于它会做我期望它会做的事情并且没有警告。询问的一个原因也是我发现这里有些缺乏提升文档,或者我只是找不到这个。我希望这是有道理的。
  • @darune 听起来很合理。顺便说一句,这是“警告”,一个词:)
【解决方案3】:

C++17 已经弃用了处理 utf 的便利函数的数量。不幸的是,最后剩下的那些将在 C++20 (*) 中被弃用。话虽如此,std::codecvt 仍然有效。从 C++11 到 C++17,您可以使用 std::codecvt&lt;char32_t, char, mbstate_t&gt;,从 C++20 开始它将是 std::codecvt&lt;char32_t, char8_t, mbstate_t&gt;

这里是一些在 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&lt;char16_t, char, std::mbstate_t&gt;std::codecvt&lt;char32_t, char, std::mbstate_t&gt;,而是 std::codecvt&lt;char16_t, char8_t, std::mbstate_t&gt;std::codecvt&lt;char32_t, char8_t, std::mbstate_t&gt;(注意 char8_t 而不是 char

【讨论】:

  • “char 将被替换为 char8_t” 可能具有误导性的陈述?
  • 您在演示中写了char16_t 而不是char32_t;故意的?
  • @LightnessRacesinOrbit:是的,在您发表评论后阅读它可能会产生误导。我已经编辑了它(并修复了愚蠢的 16 ......)。感谢您的关注。
  • @LightnessRacesinOrbit:呃……英语不是我的母语,我听不懂你最后的评论。我认为 Naaaais 很好,但 np 是什么?
  • 互联网代言“没问题”。
【解决方案4】:

在阅读了 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 的来回调用。

【讨论】:

    猜你喜欢
    • 2017-06-20
    • 1970-01-01
    • 2012-06-20
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 2015-10-08
    相关资源
    最近更新 更多