C++ std::codecvt_utf8方面存在问题

C++ std::codecvt_utf8方面存在问题,c++,visual-studio,utf-8,locale,codecvt,C++,Visual Studio,Utf 8,Locale,Codecvt,下面是一段代码,它使用std::codevt_utf8facet将wchar_t转换为UTF-8。在Visual Studio 2012中,我的期望没有得到满足(请参见代码末尾的条件)。我的期望错了吗?为什么?或者这是Visual Studio 2012库的问题 #include <locale> #include <codecvt> #include <cstdlib> int main () { std::mbstate_t state = st

下面是一段代码,它使用
std::codevt_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 ();
}
#包括
#包括
#包括
int main()
{
std::mbstate_t state=std::mbstate_t();
std::locale loc(std::locale(),新的std::codevt_utf8);
typedef std::codevt codevt_type;
codevt_type const&cvt=std::use_facet(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\u first=out\u buf;
char*out\u mid=out\u buf;
char*out\u end=out\u buf+1;
std::codecvt_base::result cvt_res
=cvt.out(状态,从\u开始,从\u结束,从\u中间,
先出、后出、中出);
//这就是我所期望的:
如果(cvt_res==std::codevt_base::partial
&&out\u mid==out\u end
&&状态!=0)
;
其他的
中止();
}
这里的期望是
out()
函数一次输出一个字节的UTF-8转换,但是上面的
if
条件中间在Visual Studio 2012中为false

更新
失败的是
out\u mid==out\u end
状态!=0
条件。基本上,我希望至少产生一个字节,并且UTF-8序列的下一个字节可产生的必要状态将存储在
state
变量中。

虽然没有直接引用它,但我认为这是
std::codevt::out
的最合乎逻辑的行为。考虑下面的场景:

  • 您使用的
    std::codecvt::out
    方式与以前相同-不将任何字符(可能不知道)翻译到
    out\buf
  • 现在,您需要将另一个字符串转换为
    out\u buf
    (再次使用
    std::codecvt::out
    )以便它附加已经在其中的内容
  • 为此,您决定使用
    buf_mid
    ,因为您知道它直接指向您在第一步翻译的字符串之后
  • 现在,如果
    std::codecvt::out
    按照您的期望工作(
    buf_-mid
    指向第一个字符之后的字符),那么您的
    out_-buf
    的第一个字符将永远不会被写入,这与您在本例中希望/期望的不完全相同
本质上,
extern\u type*&to\u next
(std::codecvt::out的最后一个参数)在这里作为您离开的位置的参考-因此您知道在哪里继续-这在您的情况下确实与您开始的位置相同(
extern\u type*to
)参数


codecvt::do_out
partial
返回代码的标准描述正好说明了这一点:

表83:

部分
并非所有源字符都已转换

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

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

在您的例子中,并非所有(零)源字符都被转换,从技术上讲,输出序列的内容(句子中的'if'子句没有输入)没有任何意义,但一般来说,“目标序列没有吸收所有可用的目标元素”,这里谈论的是有效的多字节字符。它们是由codevt\u utf8产生的多字节字符序列的元素

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

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

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

第二,看看现有的
codecvt\u utf8
:您已经了解了微软的,下面是libc++中的内容:
codecvt\u utf8::do\u out
这里在Windows上调用
ucs2\u到utf8
,在其他系统上调用
ucs4\u到utf8
,在ucs2\u到utf8(我的评论):

else if(wc<0x0800)
{
//无关
}
else//if(wc 12));
*to_nxt++=静态_cast(0x80 |((wc&0x0FC0)>>6));
*to_nxt++=静态_cast(0x80 |(wc&0x003F));
}

如果输出序列不能容纳使用一个输入宽字符产生的多字节字符,则不会写入任何内容。

还有
状态
变量/参数,该变量/参数应为实现提供有关转换状态的一些信息。您在上面描述的内容是,在对要附加的字符串再次调用
codecvt::out()
之前,应该通过状态和调用(如果输出缓冲区只有一个字节长,则可能再次在循环中调用)来处理的问题。@wilx这是一个很好的观点。Microsoft是否答复了您的问题?(我在他们的网站上看到了你的问题)除了两条自动消息之外,我还没有收到任何反应。我不知道你想反驳我的期望的哪一部分。你能澄清一下吗?@wilx你希望函数产生一个字节,而不是一个多字节
        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));
        }