寻找支持UTF8的格式化函数,如printf()等

时间:2012-02-17 09:07:04

标签: c utf-8 libc qnx

我在使用C标准库格式化函数(如sprintf())处理包含非ASCII字符的UTF-8字符串时发现了一个有趣的问题:

printf()系列的功能不知道utf-8并根据字节数而不是字符处理所有内容。因此格式不正确。

简单示例:

#include <stdio.h>

int main(int argc, char *argv[])
{
    const char* testMsg = "Tääääßt";
    char buf[1024];
    int len;

    sprintf(buf, "|%7.7s|", testMsg);
    len = strlen(buf);
    printf("Result=\"%s\", len=%d", buf, len);

    return 0;
}

结果是:

 Result="|Täää|", len=7

最有可能你们中的一些人会建议将应用程序从char转换为wchar_t并使用fwprintf()等,但由于现有的巨大应用程序,这绝对是不可能的。我可以想象编写一个在内部使用这些函数的包装器,但这会非常棘手且效率很低。

因此,最好的解决方案是替换标准C库的格式化功能的UTF-8。

目前我正在开发QNX 6.4,但回复了其他操作系统。例如Linux也非常受欢迎。

2 个答案:

答案 0 :(得分:9)

好吧,一旦你要求printf对Unicode字符进行智能填充,就会遇到重大问题。正如他们所说,

  

w͢͢͝h͡o͢͢͡͡k̵͟n̴͘ǫw̸̛s͘w̧̕a҉̡͢ţ̕ho̵r͏̵rors̡̡lį̶e͟͟͟͟in͢͢t̕h̷̡͟e͟͟d̛a͜r̕͡k̢̨̢̨h̴e͏a̷̢̡rt͏͏̴̷̵̶̵̶̸̸̷̧̧̧̛̛͘͘͘͜͜͠͏̷͏̡͝͞͞

  • Tääääßt中有多少个Unicode字符?好吧,它可以是7到11之间的任何地方,具体取决于它的编码方式。每个ä可以写成U + 00E4,这是一个字符,或者它可以写成U + 0061 U + 0308,这是两个字符。所以你的下一个希望是计算字形集群。 (不,规范化不会使问题消失。)

  • 但是,字形簇有多宽?显然,a是一列宽。 U + 200B应为零列宽,这是一个“零宽度”空间。每个ひらがな应该是两列宽吗?它们通常位于终端仿真器中。将ひらがな格式化为7列时会发生什么,是否会获得"ひらが ",这会增加一个空格,或者您获得的"ひらが"只有6列?

  • 如果您剪切混合了RTL和LTR文本的内容,您之后是否应该重置文本方向?你会怎样做? (某些终端模拟器,如Apple的,支持从左到右和从右到左文本的混合。)

  • 截断文字的目的是什么?您是在尝试向用户显示有限空间中的字符串,还是在尝试编写使用固定宽度字段的格式?

基本上,如果你想将Unicode文本剪切成块,你不应该使用像printf(或wprintf那样简单的东西,这可能更糟糕)。使用LibICU(website)迭代您想要的休息时间。编写一个支持UTF-8的printf版本就是在寻找各种你不想要的麻烦。

答案 1 :(得分:0)

以下C99代码片段定义了函数u8printf,其中格式说明符(例如%10s)产生10个utf-8代码点,即字符而不是字节。在调用此例程之前,不要忘记在setlocale(LC_ALL,&#34;&#34;)之前设置区域设置。这是有效的,因为wprintf在内部使用wchar_t。您可以用类似的方式定义u8fprintf和u8sprintf。如果你想在没有C99可变长度数组的情况下编写它,那么也可以使用malloc / free的合适组合。

int u8printf(char *fmt,...){
    va_list ap;
    va_start(ap,fmt);
        int n=mbstowcs(0,fmt,0);
        if(n==-1) return -1;
        wchar_t wfmt[n+1];
        mbstowcs(wfmt,fmt,n+1);
        for(int m=128;m<=32768;m*=2){
            wchar_t wbuf[m];
            int r=vswprintf(wbuf,m,wfmt,ap);
            if(r!=-1) {
                char buf[m*4];
                wcstombs(buf,wbuf,m*4);
                fputs(buf,stdout);
                return r;
            }
        }
        return -1;
    va_end(ap);
}