printf/fprintf/sprintf 系列在其格式说明符中支持宽度字段。我对(非宽)字符数组参数的情况有疑问:
宽度字段应该表示字节还是字符?
如果 char 数组对应于(例如)原始 UTF-8 字符串,那么(正确的事实)行为是什么?(我知道通常我应该使用一些宽字符类型,这不是重点)
例如,在
char s[] = "ni\xc3\xb1o"; // utf8 encoded "niño"
fprintf(f,"%5s",s);
该函数是否应该尝试仅输出 5 个字节(纯 C 字符)(如果两个字节导致文本字符,您将承担错位或其他问题的责任)?
还是应该尝试计算数组的“文本字符”的长度?(解码它......根据当前的语言环境?)(在这个例子中,这相当于发现字符串有 4 个 unicode 字符,所以它会添加一个填充空间)。
更新:我同意答案,printf 系列不区分纯 C 字符和字节是合乎逻辑的。问题是我的 glibc 似乎没有完全尊重这个概念,如果之前已经设置了语言环境,并且如果有(今天最常用的)LANG/LC_CTYPE=en_US.UTF-8
一个例子:
#include<stdio.h>
#include<locale.h>
main () {
char * locale = setlocale(LC_ALL, ""); /* I have LC_CTYPE="en_US.UTF-8" */
char s[] = {'n','i', 0xc3,0xb1,'o',0}; /* "niño" in utf8: 5 bytes, 4 unicode chars */
printf("|%*s|\n",6,s); /* this should pad a blank - works ok*/
printf("|%.*s|\n",4,s); /* this should eat a char - works ok */
char s3[] = {'A',0xb1,'B',0}; /* this is not valid UTF8 */
printf("|%s|\n",s3); /* print raw chars - ok */
printf("|%.*s|\n",15,s3); /* panics (why???) */
}
因此,即使设置了非 POSIX-C 语言环境,printf似乎仍然具有计算宽度的正确概念:字节(c 普通字符)而不是 unicode 字符。没关系。但是,当给定一个在他的语言环境中不可解码的 char 数组时,它会默默地恐慌(它中止 - 在第一个 '|' 之后没有打印任何内容 - 没有错误消息)......只有当它需要计算一些宽度时。我不明白为什么它甚至在不需要/必须的时候尝试从 utf-8 解码字符串。这是 glibc 中的错误吗?
使用 glibc 2.11.1 (Fedora 12) (也是 glibc 2.3.6) 测试
注意:这与终端显示问题无关 - 您可以通过管道检查输出 od :$ ./a.out | od -t cx1
这是我的输出:
0000000 | n i 303 261 o | \n | n i 303 261 | \n
7c 20 6e 69 c3 b1 6f 7c 0a 7c 6e 69 c3 b1 7c 0a
0000020 | A 261 B | \n |
7c 41 b1 42 7c 0a 7c
更新 2(2015 年 5 月):这种有问题的行为已在较新版本的 glibc 中得到修复(似乎从 2.17 开始)。有了glibc-2.17-21.fc19
它对我来说没问题。