6

mbrtowc指定NULL为(多字节字符指针)参数处理指针s,如下所示:

如果 s 是空指针,则 mbrtowc() 函数应等效于调用:

mbrtowc(NULL, "", 1, ps)

在这种情况下,参数 pwc 和 n 的值将被忽略。

据我所知,这种用法在很大程度上是无用的。如果ps不存储任何部分转换的字符,则调用将简单地返回 0 而没有副作用。如果ps正在存储一个部分转换的字符,那么由于'\0'它作为多字节序列中的下一个字节无效('\0'只能是字符串终止符),因此调用将返回(size_t)-1. errno==EILSEQps处于未定义状态。

预期的用途似乎是重置状态变量,特别是当NULL被传递ps并使用内部状态时,类似于mbtowc有状态编码的行为,但据我所知,这在任何地方都没有指定mbrtowc,而且与存储部分转换字符的语义冲突(如果mbrtowc在可能有效的初始子序列之后遇到 0 字节时重置状态,它将无法检测到这个危险的无效序列)。

如果mbrtowc指定仅在sis时重置状态变量NULL,而不是在它指向 0 字节时,则可能会出现理想的状态重置行为,但这种行为会违反所写的标准。这是标准的缺陷吗?据我所知,一旦遇到非法序列,绝对没有办法重置内部状态(在psis时使用),因此没有正确的程序可以使用with 。NULLmbrtowcps==NULL

4

2 回答 2

5

由于 '\0' 字节必须转换为空宽字符而不管移位状态(5.2.1.2 多字节字符),并且mbrtowc()指定函数在转换为宽空字符时重置移位状态(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)以指示无效的多字节字符(从而检测您描述的危险情况)。在那种情况下,转换/转换状态是未指定的(我认为你基本上已经为那个字符串而烦恼了)。尝试转换但包含 a 的多字节“序列”'\0'是无效的,并且对后续数据始终有效。如果'\0' 不打算成为转换序列的一部分,那么它不应该包含在可用于处理的字节数中。

如果您处于可能获得部分多字节字符的额外后续字节(例如来自网络流)的情况,n则为部分多字节字符传递的不应包含 0 字节,因此您将获得(size_t)(-2)回来。在这种情况下,如果您'\0'在部分转换的中间经过一段时间,您将失去存在错误的事实,并且作为副作用重置mbstate_t正在使用的状态(无论是您自己的还是正在使用的内部状态,因为您为 ) 传入了一个 NULL 指针ps。我想我在这里基本上重申了你的问题。

但是我认为可以检测和处理这种情况,但不幸的是它需要自己跟踪一些状态:

#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 函数

如果对应的宽字符是空宽字符,则描述的结果状态是初始转换状态

于 2011-01-17T06:35:48.900 回答
1

在 5.2.1.2 多字节字符中,C 标准规定:

所有位为零的字节应解释为与移位状态无关的空字符。这样的字节不应作为任何其他多字节字符的一部分出现。

该标准似乎区分了移位状态和转换状态,例如,7.24.6 提到:

指向对象描述的转换状态会根据需要更改,以跟踪关联的多字节字符序列的移位状态和多字节字符中的位置。

(重点由我添加)。但是,我认为其意图是将全零位的字节解释为空字符,而不管 mbstate_t 值如何编码整个转换状态,特别是“这样的字节不应作为任何其他多字节字符的一部分出现”意味着空字节不能出现在多字节字符中。如果在错误输入中确实出现了空字节,而多字节字符的第二个、第三个等字节应该是,那么我将标准解释为 EOF 处的部分多字节字符被静默忽略。

我对 7.24.6.3.2 的阅读,mbrtowc函数,对于 is 的情况sNULL这样的:下一个 1 字节完成空宽字符,返回值为mbrtowc0,结果状态是初始转换状态,因为:

如果对应的宽字符是空宽字符,则所描述的结果状态是初始转换状态。

通过NULL同时传递sps,内部 mbstate_tmbrtowc被重置为初始状态。

于 2011-01-25T22:14:12.360 回答