glibc的变通方法';s多字节区域设置中的printf截断错误?

glibc的变通方法';s多字节区域设置中的printf截断错误?,c,utf-8,printf,glibc,C,Utf 8,Printf,Glibc,某些基于GNU的操作系统发行版(Debian)仍然受到GNU libc中一个bug的影响,该bug导致printf函数系列在指定精度级别截断多字节字符时返回一个伪-1。此错误在2.17中修复,并向后移植到2.16。Debian对此表示支持,但维护人员似乎无意将补丁移植到Wheezy使用的2.13版本 下面的文字引自。(请不要再次在线编辑块引用。) Jonathan Nieder为这个bug提供了一个更简单的测试用例: 但不在UTF-8区域设置下,因为\277不是有效的UTF-8序列: 值得注意的

某些基于GNU的操作系统发行版(Debian)仍然受到GNU libc中一个bug的影响,该bug导致
printf
函数系列在指定精度级别截断多字节字符时返回一个伪
-1
。此错误在2.17中修复,并向后移植到2.16。Debian对此表示支持,但维护人员似乎无意将补丁移植到Wheezy使用的2.13版本

下面的文字引自。(请不要再次在线编辑块引用。)

Jonathan Nieder为这个bug提供了一个更简单的测试用例:

但不在UTF-8区域设置下,因为
\277
不是有效的UTF-8序列:

值得注意的是,在此上下文中,
printf
还将用
\0
覆盖输出数组的第一个字符

我目前正在尝试改装一个MUD代码库以支持UTF-8,不幸的是,代码中充斥着使用任意
sprintf
精度来限制发送到输出缓冲区的文本量的情况。由于大多数程序员不希望在这种上下文中返回
-1
,这可能会导致未初始化的内存读取和从中级联而来的坏消息,因此这个问题变得更加严重。(已经在valgrind抓了几个案子)


有没有人在他们的代码中为这个bug想出了一个简洁的解决方法,不涉及以任意长度精度重写格式化字符串的每次调用?我可以将截断的UTF-8字符写入我的输出缓冲区,因为在套接字写入之前,在我的输出处理中清除这些字符是相当简单的,而且在一个问题上投入如此多的精力似乎有点过火,这个问题最终会在几年后消失。

我猜,对这个问题的评论似乎证实了这一点,即您没有使用C库中所有与语言环境相关的功能。在这种情况下,最好不要将区域设置更改为基于UTF-8的区域设置,并将其保留在代码假定的单字节区域设置中

当您确实需要将UTF-8字符串作为UTF-8字符串处理时,可以使用专用代码。编写自己的UTF-8处理例程并不难。您甚至可以下载并执行一些相当复杂的字符分类。如果您更愿意使用第三方库来处理UTF-8字符串,则可以使用您在评论中提到的。不过,这是一个相当重的库,前面的一个问题推荐了一些


还可以根据需要来回切换C语言环境,以便使用C库的功能。但是,您需要检查此操作对性能的影响,因为切换区域设置可能是一项昂贵的操作。

据我所知,如果字符被截断,则根本不会输出。只有在尝试输出无效字符时,才会得到-1。有趣。在Glibc2.18上,这种行为并不存在。printf似乎将其视为一个字节字符串,就像它在C语言中一样。@ZanLynx对不起,版本号是在链接中指定的,但我应该在我的帖子中提到它们。它是在2.17中修复的,后端口为2.16,但显然Debian必须将修复后端口为Wheezy的2.13。我很好奇您的MUD代码是否有任何理由实际处理UTF-8,或者仅仅传递它就足够了?如果是这样,只需将您的区域设置强制为C,并将所有文本作为8位干净的字节缓冲区进行处理。@ZanLynx:如果您出于任何原因有一个最大长度,则这不起作用。要截断,您需要进行解释。我宁愿引入一个库而不是重新发明轮子,特别是因为我可以访问标准库未公开的Unicode字符属性。我最初的目标是避免添加依赖项,但当glibc在主要软件发行版中被破坏时,这是一厢情愿的想法。我同意ICU有点过火,但目前正在评估替代方案。将UCD表转换为C表并不太难,您可以使用自己的代码访问Unicode字符属性。尽管如此,说到底,要编写的代码总是比您意识到的要多。例如,一个人真的需要编写他们自己的实现来规范化输入字符串到NFC格式吗?我最终和你一起去了;它在简单性、可理解的文档和一组
sprintf
族映射之间提供了很好的平衡,以适应现有的基于列的格式代码。当然,不知道您需要做什么UTF-8处理,我只是指出,在您自己的代码中处理UTF-8并不难。gnulibunistring听起来很适合您的项目,但是当您找不到一个适合您的现有库时,有时候您可以使用自己的库,而不是尝试让它工作起来。我所知道的是,编写自己的代码比让GNULIBC做自己想做的事情要好。
#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;
}
$ LANG=C ./test
Author: &#65533;
printf: Success
return value: 10
$ LANG=en_US.utf8 ./test
printf: Invalid or incomplete multibyte or wide character