2012-02-17 70 views
3

处理含非ASCII字符用类似的sprintf)C标准库格式化功能(UTF8字符串时,我发现了一个有趣的问题printf()系列不知道utf-8并根据字节数处理所有内容,而不是字符。因此格式不正确。寻找如printf()UTF8感知格式化功能等

简单的例子:

#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 

最有可能有些人会向推荐应用从字符转换成wchar_t的和使用fwprintf()等,但由于现有的巨大应用程序,这绝对不可能。我可以想象写一个内部使用这些函数的包装器,但这会很棘手,效率很低。

所以最好的解决方案是标准C库格式化函数的UTF-8感知替换。

目前我正在使用QNX 6.4,但对其他操作系统的答复。例如Linux,也非常受欢迎。

+0

您的示例输出省略了前导'|'性格,这似乎不太可能反映真正发生的事情。 – unwind 2012-02-17 09:12:09

+0

@unwind你是对的,thanx。固定。 – 2012-02-17 09:13:27

+0

您可以使用Unicode库(如http://www.flexiguided.de/publications.utf8proc.en.html),并为Unicode字符串提供'printf'字节数? – trojanfoe 2012-02-17 09:15:46

回答

8

那么,一旦你要求printf做Unicode字符的智能填充,你遇到的主要问题。正如他们所说,

谁̸͢k̵͟n̴͘ǫw̸̛s͘w͘͢ḩ̵a҉̡͢t恐怖横亘在黑暗的心脏一个Unicode?͞

  • 多少Unicode字符是Tääääßt?那么,它可能在7到11之间,取决于它的编码方式。每个ä可以写成U + 00E4,它是一个字符,也可以写成U + 0061 U + 0308,它是两个字符。所以你的下一个希望是计算字形集群。 (不,标准化不会使问题消失。)

  • 但是,字形集群有多宽?显然,a是一列宽。 U + 200B应该是零列宽,这是一个“零宽度”空间。是否应该每列都是两列宽?他们通常在终端模拟器中。如果将格式设置为7列,会发生什么情况?您是否得到了"ひらが ",这会增加一个空格,还是得到"ひらが",这只有6列?

  • 如果你剪掉混合了RTL和LTR文本的东西,你应该重新设置文本方向吗?你会怎样做? (某些终端仿真程序(如Apple的)支持从左到右和从右到左的文本混合。)

  • 通过截短文本,您的目标是什么?您是否试图在有限的空间中向用户显示字符串,或者您是否尝试编写使用固定宽度字段的格式?

基本上,如果你想削减Unicode文本大块,你不应该与为printf简单的东西(或wprintf,这很可能是更坏)做这件事。使用LibICU(website)遍历你想要的中断。编写一个支持UTF-8的版本printf就是要求你不需要的各种麻烦。

+0

我想我理解你提到的问题,并且知道其中的一些在ASCII中不能令人满意地解决。不过,现在我很乐意直接替换printf(),它可以与欧洲和亚洲人物一起工作,并且不需要考虑文本方向的变化等特殊功能。关于格式截断的我的目标是固定宽度的字段。我知道,即使在“Courier”中,亚洲版的字符也不会很好,但现在这种方式对我来说很有用,直到我有时间重新设计应用程序的基于ASCII的打印。 – 2012-02-17 10:05:06

0

以下C99代码片段定义了函数u8printf,其中格式说明符(如%10s)产生10个utf-8代码点,即字符而不是字节。在调用这个例程之前,别忘了在setlocale(LC_ALL,“”)的某处设置语言环境。这是可行的,因为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); 
}