用printf显示宽字符

vmo*_*eco 6 c encoding printf widechar

我试图理解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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

*
Run Code Online (Sandbox Code Playgroud)

这里一切都很好:我的字符(*)被正确显示.

样本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);
}
Run Code Online (Sandbox Code Playgroud)

但这次没有输出,我尝试使用"编码"部分(参见上一个链接)中的许多值s[0](0xC389,201,0xC9)...但我从未得到过É显示的字符.我也试过%S而不是%ls.

如果我尝试像这样调用printf:printf("<%ls>\n", s)打印的唯一字符是'<',显示被截断.

为什么我有这个问题?我应该怎么做?

Tim*_*Tim 10

为什么我有这个问题?

请务必检查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);
}
Run Code Online (Sandbox Code Playgroud)

看输出:

$ gcc test.c && ./a.out
printf: Invalid or incomplete multibyte or wide character
Run Code Online (Sandbox Code Playgroud)

怎么修

首先,C程序的默认语言环境C(也称为POSIX)仅限ASCII.您将需要加入到呼叫setlocale,具体setlocale(LC_ALL,"").

如果你的LC_ALL,LC_CTYPE或者LANG环境变量设置为允许UTF-8空白时,你必须明确地选择一个区域.setlocale(LC_ALL, "C.UTF-8")适用于大多数系统 - C是标准的,通常实现UTF-8子集C.

#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);
}
Run Code Online (Sandbox Code Playgroud)

看输出:

$ gcc test.c && ./a.out
?
Run Code Online (Sandbox Code Playgroud)

打印出错误字符的原因是因为wchar_t表示宽字符(例如UTF-32),而不是多字节字符(例如UTF-8).请注意,wchar_tGNU 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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ gcc test.c && ./a.out
É
Run Code Online (Sandbox Code Playgroud)

请注意,您还可以LC通过命令行设置(语言环境)环境变量:

$ LC_ALL=C.UTF-8
$ ./a.out
É
Run Code Online (Sandbox Code Playgroud)


Som*_*ude 5

一个问题是您试图将 UTF-8(一种单字节编码方案)编码为多字节编码。对于 UTF-8,您使用普通char.

另请注意,由于您尝试将 UTF-8 序列组合成多字节类型,因此存在字节序(字节顺序)问题(在内存中0xC389可能以0x89and 的0xC3顺序存储)。并且编译器也会对您的数字进行符号扩展(如果sizeof(wchar_t) == 4s[0]在调试器中查看它可能是0xFFFFC389)。

另一个问题是用于打印的终端或控制台。也许它根本不支持 UTF-8 或您尝试过的其他编码?