【发布时间】:2014-10-11 22:58:34
【问题描述】:
某些基于 GNU 的操作系统发行版 (Debian) 仍然受到 GNU libc 中的一个错误的影响,该错误会导致 printf 系列函数在指定的精度级别截断多字节字符时返回虚假的 -1 .此错误已在 2.17 中修复并向后移植到 2.16。 Debian has an archived bug 对此,但维护人员似乎无意将修复程序向后移植到 Wheezy 使用的 2.13。
以下文字引用自https://sourceware.org/bugzilla/show_bug.cgi?id=6530。 (请不要再次编辑块引用内联。)
这里有一个更简单的测试用例,由 Jonathan Nieder 提供:
#include <stdio.h>
#include <locale.h>
int main(void)
{
int n;
setlocale(LC_CTYPE, "");
n = printf("%.11s\n", "Author: \277");
perror("printf");
fprintf(stderr, "return value: %d\n", n);
return 0;
}
在 C 语言环境下会做正确的事:
$ LANG=C ./test
Author: �
printf: Success
return value: 10
但不在 UTF-8 语言环境下,因为
\277不是有效的 UTF-8 序列:
$ LANG=en_US.utf8 ./test
printf: Invalid or incomplete multibyte or wide character
值得注意的是,printf 在这种情况下也会用\0 覆盖输出数组的第一个字符。
我目前正在尝试改进 MUD 代码库以支持 UTF-8,不幸的是,代码中充斥着使用任意 sprintf 精度来限制发送到输出缓冲区的文本量的情况。由于大多数程序员在这种情况下不期望返回-1,因此这个问题变得更糟,这可能导致未初始化的内存读取和由此产生的不良后果。 (已经在 valgrind 中捕获了一些案例)
有没有人为他们的代码中的这个错误想出一个简洁的解决方法,不涉及重写具有任意长度精度的格式化字符串的每一次调用?我可以将截断的 UTF-8 字符写入我的输出缓冲区,因为在套接字写入之前在我的输出处理中清理它是相当简单的,而且在最终会解决的问题上投入这么多精力似乎有点过头了再过几年。
【问题讨论】:
-
据我所知,如果字符被截断,则根本不会输出。只有在尝试输出无效字符时才会得到 -1。
-
有趣。在 glibc 2.18 上,这种行为不存在。 printf 似乎将其视为字节字符串,就像它在 C 语言中一样。
-
@ZanLynx 抱歉,链接中指定了版本号,但我应该在我的帖子中提到它们。它已在 2.17 中修复并向后移植到 2.16,但显然 Debian 有 no intention 将修复程序向后移植到 Wheezy 的 2.13。
-
我很好奇您的 MUD 代码是否有任何理由实际处理 UTF-8,或者只是传递它就足够了?如果是这样,只需将您的语言环境强制为 C 并将所有文本处理为 8 位干净的字节缓冲区。
-
@ZanLynx:如果您出于任何原因有最大长度,那将不起作用。要截断,您需要解释。