web-dev-qa-db-ja.com

printfでワイド文字を表示する

Printfがワイド文字(wchar_t)でどのように機能するかを理解しようとしています。

次のコードサンプルを作成しました。

サンプル1:

#include <stdio.h>
#include <stdlib.h>

int     main(void)
{
    wchar_t     *s;

    s = (wchar_t *)malloc(sizeof(wchar_t) * 2);
    s[0] = 42;
    s[1] = 0;
    printf("%ls\n", s);
    free(s);
    return (0);
}

出力:

*

ここではすべて問題ありません。私の文字(*)が正しく表示されています。

サンプル2:

他の種類のキャラクターを表示したかったのです。私のシステムでは、wchar_tは4バイトでエンコードされているようです。そこで、次の文字を表示しようとしました: É

#include <stdio.h>
#include <stdlib.h>

int     main(void)
{
    wchar_t     *s;

    s = (wchar_t *)malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC389;
    s[1] = 0;
    printf("%ls\n", s);
    free(s);
    return (0);
}

しかし、今回は出力がありません。s[0](0xC389、201、0xC9)の「encoding」セクション(前のリンクを参照)から多くの値を試してみました...しかし、É文字が表示されません。また、%Sの代わりに%lsを試してみました。

次のようにprintfを呼び出そうとすると、printf("<%ls>\n", s)印刷される文字は'<'だけで、表示が切り捨てられます。

なぜこの問題が発生するのですか?どうすればいいですか?

9
vmonteco

なぜこの問題が発生するのですか?

errnoprintfの戻り値を必ず確認してください。

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>

int main(void)
{
    wchar_t *s;
    s = (wchar_t *) malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC389;
    s[1] = 0;

    if (printf("%ls\n", s) < 0) {
        perror("printf");
    }

    free(s);
    return (0);
}

出力を参照してください:

$ gcc test.c && ./a.out
printf: Invalid or incomplete multibyte or wide character

直し方

まず、CプログラムのデフォルトのロケールはCPOSIXとも呼ばれます)で、ASCIIのみです。 setlocale、具体的にはsetlocale(LC_ALL,"")への呼び出しを追加する必要があります。

LC_ALLLC_CTYPE、またはLANG環境変数が空白の場合にUTF-8を許可するように設定されていない場合は、ロケールを明示的に選択する必要があります。 setlocale(LC_ALL, "C.UTF-8")はほとんどのシステムで機能します-Cは標準であり、CUTF-8サブセットは一般的に実装されています。

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main(void)
{
    wchar_t *s;
    s = (wchar_t *) malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC389;
    s[1] = 0;

    setlocale(LC_ALL, "");

    if (printf("%ls\n", s) < 0) {
        perror("printf");
    }

    free(s);
    return (0);
}

出力を参照してください:

$ gcc test.c && ./a.out
쎉

誤った文字が出力される理由は、wchar_tがマルチバイト文字(UTF-8など)ではなくワイド文字(UTF-32など)を表すためです。 wchar_tはGNU Cライブラリでは常に32ビット幅ですが、C標準ではそうである必要はありません。UTF-32BEエンコーディング(つまり0x000000C9)を使用して文字を初期化する場合は、正しく印刷されます:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main(void)
{
    wchar_t *s;
    s = (wchar_t *) malloc(sizeof(wchar_t) * 2);
    s[0] = 0xC9;
    s[1] = 0;

    setlocale(LC_ALL, "");

    if (printf("%ls\n", s) < 0) {
        perror("printf");
    }

    free(s);
    return (0);
}

出力:

$ gcc test.c && ./a.out
É

コマンドラインからLC(ロケール)環境変数を設定することもできることに注意してください。

$ LC_ALL=C.UTF-8
$ ./a.out
É
10
Tim

1つの問題は、シングルバイトエンコーディングスキームであるUTF-8をマルチバイトエンコーディングとしてエンコードしようとしていることです。 UTF-8の場合、プレーンcharを使用します。

また、UTF-8シーケンスをマルチバイトタイプに結合しようとするため、 エンディアン (バイトオーダー)の問題が発生することにも注意してください(メモリ内では、0xC3890x89および0xC3として格納される可能性があります。注文)。 そしてコンパイラがあなたの番号も符号拡張すること(sizeof(wchar_t) == 4とデバッガでs[0]を見ると、 0xFFFFC389)。

もう1つの問題は、印刷に使用する端末またはコンソールです。たぶんそれは単にUTF-8またはあなたが試した他のエンコーディングをサポートしていませんか?