2012-02-17 20 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); 
}