std :: codecvt_utf8方面的问题
以下是使用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.
失败的条件是out_mid == out_end
和state != 0
条件。基本上,我希望至少生成一个字节,并且可以生成UTF-8序列的下一个字节的必要状态存储在变量state
中。
的codecvt::do_out
partial
返回码的标准描述完全这样说:
在表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));
}
如果输出序列无法适应消耗一个输入宽字符导致的多字节字符,则不会写入输出序列。
尽管没有直接引用它,但我认为这是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
)参数。
我不确定你想要反驳的是我期望的部分。你能澄清一下吗? – wilx
@wilx您希望函数产生一个字节,而不是多字节字符。它从来没有被指定能够做到这一点,类似的功能,以及现有的实现被指定为不这样做。 – Cubbi
假设你是对的,那么缓冲区应该有多大呢? '的std :: ::的codecvt MAX_LENGTH()'? – wilx