2013-10-14 256 views
4

以下是使用std::codecvt_utf8<>方面从wchar_t转换为UTF-8的代码片段。使用Visual Studio 2012,我的期望不符合(请参阅代码末尾的条件)。我的期望错了吗?为什么?或者这是一个Visual Studio 2012库问题?std :: codecvt_utf8方面的问题

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

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

UPDATE

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

回答

4

codecvt::do_outpartial返回码的标准描述完全这样说:

在表83:

partial不是所有的源字符转换

在22.4.1.4.2 [locale.codecvt。虚函数]/5:

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

在你的情况,不是所有的(零)源字符进行了改装,在技术上也不说的输出序列的内容(“如果”中的句子子句不能进入),但总体来讲,“目的地序列没有吸收所有可用的目标元素“,这里讲的是有效的多字节字符。它们是由codecvt_utf8产生的多字节字符序列的元素

这将是很好有一个更明确的标准写法,但这里是两个证据间接件:

一:旧的C的宽到多字节转换功能std::wcsrtombs(其特定于语言环境的变体通常是通过codecvt::do_out为系统提供的语言环境)的现有实现所谓的定义如下:

转换停止。[...]当下次多字节字符会超过LEN总字节数的限制将被存储到阵列由dst指出。

两个,看看codecvt_utf8现有的实现:你已经探索了微软,这里是什么了libC++:codecvt_utf8::do_out这里呼吁ucs2_to_utf8在Windows和ucs4_to_utf8在其他系统上,并ucs2_to_utf8 does the following(评论我的):

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

如果输出序列无法适应消耗一个输入宽字符导致的多字节字符,则不会写入输出序列。

+0

我不确定你想要反驳的是我期望的部分。你能澄清一下吗? – wilx

+0

@wilx您希望函数产生一个字节,而不是多字节字符。它从来没有被指定能够做到这一点,类似的功能,以及现有的实现被指定为不这样做。 – Cubbi

+0

假设你是对的,那么缓冲区应该有多大呢? '的std :: ::的codecvt MAX_LENGTH()'? – wilx

2

尽管没有直接引用它,但我认为这是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)参数。

+0

还有'状态'变量/参数应该给实施一些关于转换状态的信息。你上面描述的是恕我直言的应该通过状态照顾和调用['codecvt :: unshift()'](http://en.cppreference.com/w/cpp/locale/codecvt/unshift)(如果输出缓冲区只有一个字节长,则可能再次循环),然后再次调用'codecvt :: out()'作为附加字符串。 – wilx

+0

@wilx这是一个很好的观点。 Microsoft回答了您的问题了吗? (我在他们的网站上看到你的问题) –

+0

我还没有收到任何反应,除了两个自动化的消息,但。 – wilx