Printfがワイド文字(wchar_t
)でどのように機能するかを理解しようとしています。
次のコードサンプルを作成しました。
#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);
}
出力:
*
ここではすべて問題ありません。私の文字(*
)が正しく表示されています。
他の種類のキャラクターを表示したかったのです。私のシステムでは、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)
印刷される文字は'<'
だけで、表示が切り捨てられます。
なぜこの問題が発生するのですか?どうすればいいですか?
errno
とprintf
の戻り値を必ず確認してください。
#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プログラムのデフォルトのロケールはC
(POSIX
とも呼ばれます)で、ASCIIのみです。 setlocale
、具体的にはsetlocale(LC_ALL,"")
への呼び出しを追加する必要があります。
LC_ALL
、LC_CTYPE
、またはLANG
環境変数が空白の場合にUTF-8を許可するように設定されていない場合は、ロケールを明示的に選択する必要があります。 setlocale(LC_ALL, "C.UTF-8")
はほとんどのシステムで機能します-C
は標準であり、C
のUTF-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
É
1つの問題は、シングルバイトエンコーディングスキームであるUTF-8をマルチバイトエンコーディングとしてエンコードしようとしていることです。 UTF-8の場合、プレーンchar
を使用します。
また、UTF-8シーケンスをマルチバイトタイプに結合しようとするため、 エンディアン (バイトオーダー)の問題が発生することにも注意してください(メモリ内では、0xC389
は0x89
および0xC3
として格納される可能性があります。注文)。 そしてコンパイラがあなたの番号も符号拡張すること(sizeof(wchar_t) == 4
とデバッガでs[0]
を見ると、 0xFFFFC389
)。
もう1つの問題は、印刷に使用する端末またはコンソールです。たぶんそれは単にUTF-8またはあなたが試した他のエンコーディングをサポートしていませんか?