std::codecvt_utf8 方面出现问题

wil*_*ilx 5 c++ locale utf-8 visual-studio codecvt

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

#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 ();
}
Run Code Online (Sandbox Code Playgroud)

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

更新

失败的是out_mid == out_endstate != 0条件。基本上,我希望至少生成一个字节,并且将生成 UTF-8 序列的下一个字节所需的状态存储在变量中state

Cub*_*bbi 4

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

在表83中:

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

在 22.4.1.4.2[locale.codecvt.virtuals]/5 中:

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

在您的情况下,并非所有(零)源字符都被转换,这从技术上讲没有说明输出序列的内容(未输入句子中的“if”子句),但一般来说,“目标序列没有吸收所有可用的目标元素”这里谈论有效的多字节字符。它们是由 生成的多字节字符序列的元素codecvt_utf8

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

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

当下一个多字节字符超出要存储到 dst 指向的数组中的 len 总字节数限制时,转换停止 [...]。

第二,看看现有的实现codecvt_utf8:您已经探索了 Microsoft 的实现,这是 libc++ 中的内容:这里在 Windows 和其他系统上codecvt_utf8::do_out调用,ucs2_to_utf8执行以下操作(我的评论):ucs2_to_utf8ucs4_to_utf8

        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));
        }
Run Code Online (Sandbox Code Playgroud)

如果输出序列无法容纳因消耗一个输入宽字符而产生的多字节字符,则不会向输出序列写入任何内容。