c - mbrtowc 的 s==NULL 案例的目的是什么?

标签 c standards multibyte language-lawyer

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

If s is a null pointer, the mbrtowc() function shall be equivalent to the call:

mbrtowc(NULL, "", 1, ps)

In this case, the values of the arguments pwc and n are ignored.

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

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

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

最佳答案

由于无论移位状态如何,'\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) 以指示无效的多字节char(从而检测到您描述的危险情况)。在那种情况下,转换/转换状态是未指定的(我认为你基本上已经被那个字符串所淹没了)。尝试转换但包含 '\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 多字节字符

  • A multibyte character set may have a state-dependent encoding, wherein each sequence of multibyte characters begins in an initial shift state and enters other locale-specific shift states when specific multibyte characters are encountered in the sequence. While in the initial shift state, all single-byte characters retain their usual interpretation and do not alter the shift state. The interpretation for subsequent bytes in the sequence is a function of the current shift state.

  • A byte with all bits zero shall be interpreted as a null character independent of shift state.

  • A byte with all bits zero shall not occur in the second or subsequent bytes of a multibyte character.

7.24.6.3.2/3 mbrtowc函数

If the corresponding wide character is the null wide character, the resulting state described is the initial conversion state

关于c - mbrtowc 的 s==NULL 案例的目的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4709498/

相关文章:

c - 如何使用ImageMagick C API的MagickGetImageHistogram

c - 与聚合或 union 类型相关的严格别名

java - 标准方法名,为什么println中的l不是大写?

regex - 检测 Lua 中的最后一个字符是否不是多字节

php - 在哪里可以获得 PHP 的所有多字节函数的完整列表?

c++:获取宽字符的ascii值

c - 需要帮助读取文件并将内容存储到二维数组中

c - 如何获取c程序的执行时间?

c - 如何查找当前目录占用的磁盘 block 总数(C UNIX)?

sql - COALESCE 或 CASE 更高效和/或标准