C99标准 - fprintf - s精确转换

时间:2016-09-25 12:24:16

标签: c unicode c99

我们假设只有C99 Standard论文和printf库函数需要根据此标准实现才能使用UTF-16编码,请您澄清一下指定精度的s转换的预期行为?

s转换的C99标准版(7.19.6.1)说:

  

如果不存在l length修饰符,则参数应该是指向字符类型数组的初始元素的指针。数组中的字符被写入(但不包括)终止空字符。如果指定了精度,则不会写入多少个字节。如果未指定精度或大于数组的大小,则数组应包含空字符。

     

如果存在l长度修饰符,则参数应该是指向wchar_t类型数组的初始元素的指针。数组中的宽字符将转换为多字节字符(每个字符就好像通过调用wcrtomb函数一样,在转换第一个宽字符之前将mbstate_t对象描述的转换状态初始化为零),直到并包括终止的null宽字符。生成的多字节字符被写入(但不包括)终止空字符(字节)。如果未指定精度,则数组应包含空宽字符。如果指定了精度,则写入的字节数不会超过(包括移位序列,如果有的话),并且如果要等于精度给出的多字节字符序列长度,则该数组应包含空宽字符,该函数需要在数组末尾访问一个宽字符。在任何情况下都不会写出部分多字节字符。

我一般都不太了解这一段和语句"如果指定了一个精度,那么写入的字节数不会多于#34;特别是。

例如,让我们采用UTF-16字符串" TEST" (字节序列:0x54,0x00,0x45,0x00,0x53,0x00,0x54,0x00)。

在以下情况下,期望写入输出缓冲区:

  • 如果精度为3
  • 如果 precision 为9(比字符串长度多一个字节)
  • 如果 precision 为12(比字符串长度多几个字节)

然后还有#34;阵列中的宽字符被转换为多字节字符"。这是否意味着首先应将UTF-16转换为UTF-8?如果我希望只使用UTF-16,这很奇怪。

2 个答案:

答案 0 :(得分:1)

将评论转换为稍微扩展的答案。

您的实施中CHAR_BIT的价值是多少?

  • 如果CHAR_BIT == 8,您无法使用%s处理UTF-16;您使用%ls并且您将wchar_t *作为相应的参数传递。然后,您必须阅读规范的第二段。

  • 如果CHAR_BIT == 16,则数据中不能有奇数个八位字节。然后你需要知道wchar_tchar的关系(它们是否具有相同的大小?它们是否具有相同的符号?)并解释两个段落以产生统一的效果 - 除非您决定让wchar_t代表UTF-32。

关键是如果CHAR_BIT == 8不能将UTF-16作为C字符串处理,因为有太多有用的字符用一个字节保持零编码,但这些零字节标记为空的结尾 - 终止字符串。要处理UTF-16,普通char类型必须是16位(或更大)类型(因此CHAR_BIT > 8),或者您必须使用wchar_t(和{{ 1}})。

请注意,规范要求将宽字符转换为合适的多字节表示。

如果您希望本地输出宽字符,则必须使用sizeof(wchar_t) > sizeof(char)中的fwprintf()及相关函数,该函数首先在C99中定义。那里的规范与<wchar.h>的规范有许多共同之处,但有(不出所料)重要的差异。

  

7.29.2.1 fwprintf函数

     

...

     

fprintf()
  如果不存在s长度修饰符,则参数应为指向初始值的指针   包含多字节字符序列的字符数组的元素   从初始换档状态开始。数组中的字符转换为   如果通过重复调用l函数,转换状态   由mbrtowc对象描述,在第一个之前初始化为零   多字节字符被转换,并写入(但不包括)   终止null宽字符。如果指定精度,则不超过   写了很多宽字。如果未指定精度或是   大于转换数组的大小,转换后的数组应包含一个   null宽字符。

     

如果存在mbstate_t长度修饰符,则参数应为指向初始值的指针   l类型数组的元素。数组中的宽字符是   写入(但不包括)终止空宽字符。如果   指定精度,不超过写入多个宽字符。如果   未指定精度或大于数组(数组)的大小   应包含空宽字符。

答案 1 :(得分:1)

wchar_t不适用于UTF-16,仅适用于实现定义的固定宽度编码,具体取决于当前的语言环境。使用宽字符API支持可变长度编码根本没有理智的方法。同样,printfwcrtomb等函数使用的多字节表示是实现定义的。如果要使用Unicode编写可移植代码,则不能依赖宽字符API。使用库或滚动自己的代码。

要回答您的问题:fprintf l修饰符接受当前语言环境指定的实现定义编码中的宽字符串。如果wchar_t是16位,这种编码可能是UTF-16的混蛋,但正如我上面提到的,没有办法正确支持UTF-16代理。然后,将此wchar_t字符串转换为实现定义编码中的多字节char字符串。这可能是也可能不是UTF-8。指定的精度限制了输出字符串中char的数量,并增加了不写入部分多字节字符的限制。

这是一个例子。假设宽字符编码是带有32位wchar_t的UTF-32,并且多字节编码是UTF-8(就像在带有appropriate locale的Linux上一样)。以下代码

wchar_t w[] = { 0x1F600, 0 }; // U+1F600 GRINNING FACE
printf("%.3ls", w);

将完全不打印,因为生成的UTF-8序列有四个字节。仅当您指定至少为4的精度

printf("%.4ls", w);

将打印角色。

编辑:要回答第二个问题,不,printf永远不应该写一个空字符。该句子仅表示在某些情况下,需要一个空字符来指定字符串的结尾并避免缓冲区过读。