printf/fprintf/sprintf系列函数支持在格式说明符中使用宽度字段。对于(非宽字符)char数组参数,我有一个疑问:
宽度字段是指字节还是字符?
如果char数组对应于(比如)原始的UTF-8字符串,则是什么(正确的事实上的)行为?(我知道通常应该使用某些宽字符类型,但这不是重点)
例如,在
char s[] = "ni\xc3\xb1o"; // utf8 encoded "niño"
fprintf(f,"%5s",s);
这个函数是试图输出仅有5个字节(普通C字符)吗?(如果两个字节结果是文本字符,则您需要承担不对齐或其他问题的责任)?
还是它试图计算数组的“文本字符”长度?(根据当前区域设置进行解码?)
(在此示例中,这将导致找出字符串有4个Unicode字符,因此会添加填充空格。)
更新:我同意答案,printf系列不区分普通C字符和字节是合理的。问题是,如果先前设置了区域设置,并且如果使用(今天最常用的)LANG/LC_CTYPE=en_US.UTF-8,则我的glibc似乎没有完全遵守这个概念。
实例:
#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 plain chars)而不是Unicode字符为单位。这很好。但是,当给出一个在他的语言环境中无法解码的字符数组时,它会静默恐慌(它会中止 - 第一个“|”后没有打印任何内容 - 没有错误消息)......只有当它需要计算某些宽度时才会发生。我不明白为什么它甚至尝试从utf-8解码字符串,当它不需要/没有必要。这是glibc的bug吗?
测试使用glibc 2.11.1(Fedora 12)(也是glibc 2.3.6)
注意:这与终端显示问题无关 - 您可以通过管道输出进行检查:$ ./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
版本中,它可以正常工作。