首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >std::codecvt_utf8方面的问题

std::codecvt_utf8方面的问题
EN

Stack Overflow用户
提问于 2013-10-14 09:18:52
回答 2查看 3K关注 0票数 5

下面是使用std::codecvt_utf8<> facet将wchar_t转换为UTF-8的代码片段。在Visual 2012中,我的期望没有得到满足(请参阅代码末尾的条件)。我的期望是错的吗?为什么?还是这是Visual 2012库问题?

代码语言:javascript
复制
#include <locale>
#include <codecvt>
#include <cstdlib>

int main ()
{
    std::mbstate_t state = std::mbstate_t ();
    std::locale loc (std::locale (), new std::codecvt_utf8<wchar_t>);
    typedef std::codecvt<wchar_t, char, std::mbstate_t> codecvt_type;
    codecvt_type const & cvt = std::use_facet<codecvt_type> (loc);

    wchar_t ch = L'\u5FC3';
    wchar_t const * from_first = &ch;
    wchar_t const * from_mid = &ch;
    wchar_t const * from_end = from_first + 1;

    char out_buf[1];
    char * out_first = out_buf;
    char * out_mid = out_buf;
    char * out_end = out_buf + 1;

    std::codecvt_base::result cvt_res
        = cvt.out (state, from_first, from_end, from_mid,
            out_first, out_end, out_mid);

    // This is what I expect:
    if (cvt_res == std::codecvt_base::partial
        && out_mid == out_end
        && state != 0)
        ;
    else
        abort ();
}

这里的期望是,out()函数一次输出一个字节的UTF-8转换,但是上面if条件的中间在Visual 2012中是假的。

更新

失败的是out_mid == out_endstate != 0条件。基本上,我预计至少会产生一个字节,并且需要将UTF-8序列的下一个字节存储在state变量中。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-10-17 20:44:11

partial返回代码的标准描述codecvt::do_out就是这样说的:

在表83中:

partial并非所有源字符都已转换

在22.4.1.4.2 locale.codecvt.virtuals/5中:

返回:枚举值,如表83所总结。partial的返回值(如果是(from_next==from_end) )表示目标序列没有吸收所有可用的目标元素,或者在生成另一个目标元素之前需要额外的源元素。

在您的示例中,并不是所有(零)源字符都被转换了,这在技术上没有提到输出序列的内容(没有输入句子中的'if‘子句),但一般来说,“目标序列没有吸收所有可用的目标元素”,这里讨论的是有效的多字节字符。它们是codecvt_utf8产生的多字节字符序列的元素。

最好有一个更明确的标准措辞,但这里有两个间接证据:

第一:旧C的宽到多字节转换函数std::wcsrtombs (其特定于地区的变体通常由codecvt::do_out的现有实现为系统提供的地区调用)定义如下:

转换停止..。当下一个多字节字符将超过存储到dst所指向的数组中的len总字节的限制时。

第二,查看codecvt_utf8的现有实现:您已经了解了微软的实现,下面是libc++中的内容:codecvt_utf8::do_out在Windows上调用ucs2_to_utf8,在其他系统上调用ucs4_to_utf8,在其他系统上调用ucs2_to_utf8 执行以下操作 (注释):

代码语言:javascript
复制
        else if (wc < 0x0800)
        {
            // not relevant
        }
        else // if (wc <= 0xFFFF)
        {
            if (to_end-to_nxt < 3)
                return codecvt_base::partial; // <- look here
            *to_nxt++ = static_cast<uint8_t>(0xE0 |  (wc >> 12));
            *to_nxt++ = static_cast<uint8_t>(0x80 | ((wc & 0x0FC0) >> 6));
            *to_nxt++ = static_cast<uint8_t>(0x80 |  (wc & 0x003F));
        }

如果输出序列不能适应由于使用一个输入宽字符而导致的多字节字符,则不会写入任何输出序列。

票数 4
EN

Stack Overflow用户

发布于 2013-10-17 18:55:11

虽然没有直接引用它,但我认为这是std::codecvt::out最符合逻辑的行为。考虑以下情况:

  • 您使用std::codecvt::out的方式与以前一样--没有将任何字符(可能不知道)翻译到out_buf中。
  • 现在,您希望将另一个字符串转换为您的out_buf (再次使用std::codecvt::out),以便将已经在其中的内容附加到
  • 为此,您决定使用您的buf_mid,因为您知道它直接指向您在第一步中翻译的字符串之后。
  • 现在,如果std::codecvt::out按照您的期望工作(buf_mid指向第一个之后的字符),那么您的out_buf的第一个字符就永远不会被写入--在这种情况下,这不是您想要的/期望的。

本质上,extern_type*& to_next (std::codecvt::out的最后一个参数)在这里是作为对您离开的位置的引用--所以您知道在哪里继续--在您的情况下,这个位置与开始位置(extern_type* to)参数是相同的。

  • cppreferece.com on std::codecvt::out
  • cpulusplus.com on std::codecvt::out
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19357027

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档