以下是使用std::codecvt_utf8<>
构面从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 ();
}
这里的期望是out()
函数一次输出UTF-8转换的一个字节,但是Visual Studio 2012上面if
条件的中间值为false。
失败的是out_mid == out_end
和state != 0
条件。基本上,我希望至少生成一个字节,并且可以生成UTF-8序列的下一个字节的必要状态存储在state
变量中。
答案 0 :(得分:4)
partial
codecvt::do_out
返回代码的标准说明正好说明了这一点:
:
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 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));
}
如果输出序列无法容纳因使用一个输入宽字符而产生的多字节字符,则不会写入任何内容。
答案 1 :(得分: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
)参数一样。