3

非 ASCII 文字を含む UTF-8 文字列を sprintf() のような C 標準ライブラリの書式設定関数で処理するときに、興味深い問題を発見しました。

printf() ファミリの関数は utf-8 を認識せず、char ではなくバイト数に基づいてすべてを処理します。したがって、フォーマットが正しくありません。

簡単な例:

#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 なども大歓迎です。

4

2 に答える 2

10

さて、printfUnicode 文字のインテリジェントなパディングを要求すると、大きな問題が発生します。彼らが言うように、

w͢͢͝h͡o͢͡k̵͟k̵͟n̴͘ǫw̸̛s͘w͘͢ḩ̵w͘͢ḩ̵a҉̡͢ţ̕h́o

  • にはいくつの Unicode 文字がありTääääßtますか? エンコード方法に応じて、7 から 11 のいずれかになります。それぞれäは、1 文字の U+00E4 と書くことも、2 文字の U+0061 U+0308 と書くこともできます。次の希望は、書記素クラスターを数えることです。(いいえ、正規化しても問題は解決しません。)

  • しかし、書記素クラスターの幅はどれくらいですか? 明らかに、a幅は 1 列です。U+200B はゼロ列幅である必要があります。これは「幅ゼロ」のスペースです。各ひらがなは 2 列幅にする必要がありますか? それらは通常、端末エミュレータにあります。ひらがなを 7 列にフォーマットするとどうなり"ひらが "ます"ひらが"か?

  • RTL と LTR のテキストが混在するものを切り取った場合、後でテキストの方向を再設定する必要がありますか? 何をする?(Apple などの一部の端末エミュレーターは、左から右へのテキストと右から左へのテキストの混合をサポートします。)

  • テキストを切り捨てることによるあなたの目標は何ですか? 限られたスペースでユーザーに文字列を表示しようとしていますか、それとも固定幅フィールドを使用するフォーマットを記述しようとしていますか?

基本的に、Unicode テキストをチャンクに分割したい場合は、printf(またはwprintfのような単純なもので行うべきではありません。これはおそらくもっと悪いことです)。LibICU (ウェブサイト) を使用して、必要なブレークを繰り返します。の UTF-8 対応バージョンをprintf作成すると、望ましくないあらゆる種類の問題が発生します。

于 2012-02-17T09:28:39.060 に答える
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);
}
于 2014-05-06T19:32:42.267 に答える