mbrtowc
来处理NULL
(多字节字符指针)参数的s
指针,如下所示:
如果s是空指针,则mbrtowc()函数应等同于调用:
mbrtowc(NULL, "", 1, ps)
在这种情况下,参数pwc和n的值将被忽略。
据我所知,这种用法基本没用。如果ps
没有存储任何部分转换的字符,则调用将返回0而没有副作用。如果ps
正在存储部分转换的字符,那么由于'\0'
无效,因为多字节序列中的下一个字节('\0'
只能是字符串终止符),该调用将返回(size_t)-1
errno==EILSEQ
ps
。并使NULL
处于未定义状态。
预期的用法似乎是重置状态变量,特别是在为ps
传递mbtowc
并且已使用内部状态时,类似于mbrtowc
的行为有状态编码,但据我所知,未在任何地方指定,并且它与mbrtowc
存储部分转换字符的语义冲突(如果mbrtowc
当在可能有效的初始子序列之后遇到0字节时重置状态,它将无法检测到这种危险的无效序列。)
如果指定s
仅在NULL
为ps
时重置状态变量,而在指向0字节时不重置状态变量,则可能出现所需的状态重置行为,但这种行为会违反书面标准。这是标准中的缺陷吗?据我所知,一旦遇到非法序列,绝对没有办法重置内部状态(在NULL
为mbrtowc
时使用),因此没有正确的程序可以使用{{1与ps==NULL
。
答案 0 :(得分:5)
由于'\ 0'字节必须转换为空宽字符而不管移位状态(5.2.1.2多字节字符),并且指定mbrtowc()
函数以在转换为宽时重置移位状态null字符(7.24.6.3.2 / 3 mbrtowc函数),调用mbrtowc( NULL, "", 1, ps)
将重置存储在mbstate_t
所指向的ps
中的移位状态。如果调用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多字节字符
多字节字符集可以具有依赖于状态的编码,其中 每个字节的多字节字符 从初始换档状态开始 进入其他特定于语言环境的班次 特定多字节时的状态 在中遇到的字符 序列。而在最初的转变 state,所有单字节字符 保留他们通常的解释和 不要改变换档状态。该 对后续字节的解释 序列是一个函数 当前的转变状态。
所有位为零的字节应解释为空字符 独立于班次状态。
- 所有位为零的字节不得出现在第二个或后续的位置 多字节字符的字节。
7.24.6.3.2 / 3 mbrtowc函数
如果相应的宽字符是 null宽字符,由此产生 描述的状态是最初的 转换状态
答案 1 :(得分:1)
在5.2.1.2,多字节字符中,C标准规定:
所有位为零的字节应解释为与移位无关的空字符 州。这样的字节不应作为任何其他多字节字符的一部分出现。
标准似乎区分了班次状态和转换状态,例如,7.24.6提及:
指向对象描述的转换状态根据需要进行更改,以跟踪关联的多字节字符序列的移位状态和多字节字符中的位置。
(我强调)。但是,我认为目的是将所有零位的字节解释为空字符,而不管mbstate_t值如何编码整个转换状态,特别是“这样的字节不应作为任何其他多字节字符的一部分出现”意味着空字节不能出现在多字节字符中。如果在错误输入中出现空字节 ,其中多字节字符的第二个,第三个等字节应该是,那么我将标准解释为说EOF中的部分多字节字符被默默忽略
对于mbrtowc
为s
的情况,我对7.24.6.3.2,NULL
函数的解读是:下一个1字节完成空宽字符,返回mbrtowc
的值为0,结果状态为初始转换状态,因为:
如果相应的宽字符是空宽字符,则描述的结果状态是初始转换状态。
通过NULL
和s
传递ps
,mbrtowc
的内部mbstate_t将重置为初始状态。