因为无论移位状态如何,'\0' 字节都必须转换为空宽字符(5.2.1.2 多字节字符),并且指定mbrtowc() 函数在转换为宽空字符时重置移位状态(7.24.6.3.2/3 mbrtowc 函数),调用mbrtowc( NULL, "", 1, ps) 将重置存储在ps 指向的mbstate_t 中的移位状态。如果调用mbrtowc( NULL, "", 1, NULL) 以使用库的内部mbstate_t 对象,它将被重置为初始状态。有关标准相关部分的引用,请参见答案的末尾。
我对 C 标准多字节转换函数并不是特别有经验(我对这种事情的经验是使用 Win32 API 进行转换)。
如果 mbrtowc() 处理一个被 0 字节缩短的“不完整字符”,它应该返回 (size_t)(-1) 以指示无效的多字节字符(从而检测您描述的危险情况)。在那种情况下,转换/转换状态是未指定的(我认为你基本上已经为那个字符串而烦恼了)。尝试转换但包含'\0' 的多字节“序列”无效,并且对后续数据始终有效。如果'\0' 不打算成为转换序列的一部分,那么它不应该包含在可用于处理的字节数中。
如果您可能会获得部分多字节字符的额外后续字节(例如来自网络流),则为部分多字节字符传递的 n 不应包含 0 字节,所以你会得到一个(size_t)(-2) 返回。在这种情况下,如果您在部分转换过程中传递了'\0',您将失去存在错误的事实,并且作为副作用重置使用中的mbstate_t 状态(无论是您自己的还是内部使用的是因为您为ps 传递了一个NULL 指针)。我想我基本上是在这里重申你的问题。
但是我认为可以检测和处理这种情况,但不幸的是它需要自己跟踪一些状态:
#define MB_ERROR ((size_t)(-1))
#define MB_PARTIAL ((size_t)(-2))
// function to get a stream of multibyte characters from somewhere
int get_next(void);
int bar(void)
{
char c;
wchar_t wc;
mbstate_t state = {0};
int in_partial_convert = 0;
while ((c = get_next()) != EOF)
{
size_t result = mbrtowc( &wc, &c, 1, &state);
switch (result) {
case MB_ERROR:
// this multibyte char is invalid
return -1;
case MB_PARTIAL:
// do nothing yet, we need more data
// but remember that we're in this state
in_partial_convert = 1;
break;
case 1:
// output the competed wide char
in_partial_convert = 0; // no longer in the middle of a conversion
putwchar(wc);
break;
case 0:
if (in_partial_convert) {
// this 'last' multibyte char was mal-formed
// return an error condidtion
return -1;
}
// end of the multibyte string
// we'll handle similar to EOF
return 0;
}
}
return 0;
}
也许不是一个理想的情况,但我认为它表明它没有完全损坏以致无法使用。
标准引用:
5.2.1.2 多字节字符
7.24.6.3.2/3 mbrtowc 函数
如果对应的宽字符是
空宽字符,结果
描述的状态是初始状态
转化状态