如何用'%s'说明符打印一个unicode字符串?

时间:2018-04-27 17:11:02

标签: c unicode printf

我正在尝试使用printf()调用%s输出Unicode字符/字符串,但它不会打印任何内容。

如果我这样打printf()

 printf("\xE2\x98\xA0")

我得到

但是,如果我像这样使用%ls

printf("%ls", "☠")  /* or */
printf("%ls", L"☠") /* or */
printf("%ls", L"\xE2\x98\xA0")

我什么都没打印;

另外,如何在其中声明带有Unicode字符的wchar_t字符串? wchar_t wstro[50] = L"☠"不起作用。

我需要malloc()一个wchar_t然后将Unicode数据放入其中吗?

2 个答案:

答案 0 :(得分:3)

您将Unicode与UTF-8混淆,并且两者都与wchar_t混淆。

Unicode是一种抽象的东西,带有代码点,结合了字符和其他属性。

UTF-8是编码Unicode的常用方法,它与ASCII兼容(仅限ASCII字符串),并且与C字符串兼容(因此零终止(字符串中没有其他0字节)。 \xE2\x98\xA0是UTF-8表示。

字符可能也以UTF-8编码。这取决于您的编辑器,但编辑通常不使用wchar_t。

所以:使用UTF-8,你应该使用%s而不是%ls。所以你的3次尝试都是错误的。

我是一般的,使用UTF-8等char*和普通的字符串函数(只是在随机字节处不断字符串,但这意味着如果遵循随机UTF-8代码点也不会破坏字符串通过一些组合代码点。

您可以使用wchar_t,但通常使用使用wchar_t的协议,但特别是在这种情况下,您应该格外小心,因为wchar_t的大小无法与所需的字符大小(预期编码的大小)兼容[例如你的系统,所以wchar_t可能只有2个字节,但你可以使用UCS2,但不能使用UTF-32,或者相反,如果系统将wchar_t定义为4个字节)。

因此,请保持简单并尝试仅使用UTF-8,并将其用作普通的C字符串。

答案 1 :(得分:2)

此答案假定您使用的是MS Windows

我们非常难过,我们在2018年,这些东西仍然无法正常工作。但事情就是这样:

printf("\xE2\x98\xA0");(与printf("%s", "\xE2\x98\xA0");相同)有效,因为您只输出3个字符到输出流。 C语言中没有出现Unicode或特殊字符处理。您的终端环境在输出中查找UTF-8字符串并相应地选择显示字形。

同样,如果您将输出写入文件(使用fprintf或流重定向),您会看到该文件包含0xE2, 0x98, 0xA0,然后您可以选择使用转换UTF的文本文件查看器-8显示字形。

这部分都很好,您可以(也可能应该)编写程序,只将UTF-8编码的字符写入FILE流。

当我们想要输出wchar_t个字符时,问题就开始了。理论上这应该有效:

printf("%ls", L"\u2620");   

应该发生的是调用wcstombs将unicode代码点序列转换为多字节序列。但是使用哪种多字节格式? UTF-8现在已经无处不在,但过去还有其他格式,如ShiftJIS,Big-5等。

您必须使用setlocale指定多字节格式。 locales的细节是实现定义的。

这是踢球者。 Windows支持的一般UTF-8输出没有C语言环境。如果你尝试setlocale(LC_CTYPE, ".65001");,它就行不通。

您可以使用受支持的区域设置输出Unicode的某些子集。例如the MSDN example使用Japanese_Japan.932工作,输出Unicode输入为Shift-JIS。 (不是UTF-8)。

更糟糕的是,如果您使用Windows API函数WideStringToMultiByte,则 接受CP_UTF8的“区域设置”。您可以使用此函数将L"\u2620";转换为char缓冲区,并将printf转换为生成UTF-8输出。

但当然,您无法“插入”FILE流处理,只会调用wcstombs而不是WideStringToMultiByte

为什么他们不允许".UTF-8"作为wcstombs的区域设置?恶意行为?谁知道呢。

理论上应该起作用的下一件事是:

FILE *fp = fopen("a.txt", "w");
fwide(fp, 1);
fwprintf(fp, L"\u2620");

但实际上,MS运行时实际上并没有对fwide做任何事情;它不支持面向广播的流。 wprintf系列的Microsoft实现实际上只输出窄字符,而不是宽字符,并且它们使用与narrow printf系列相同的wcstombs方法。

因此,该代码不起作用,日本wcstombs示例中的代码fwprintf(fp, L"\u3603");(使用.932 CP集)输出多字节序列而不是原始宽字符。

要通过stdio.h API编写UTF-16文件,您实际上别无选择,只能使用窄字符并将其视为二进制文件。