这个答案假设你正在使用MS Windows操作系统
很遗憾,我们已经进入2018年,但这些东西仍然不能正常工作。但是事情的现状是这样的:
printf("\xE2\x98\xA0");
(与
printf("%s", "\xE2\x98\xA0");
相同)可以工作,因为你只是将3个字符输出到输出流中。在C语言中没有Unicode或特殊字符处理。是终端环境寻找输出中的UTF-8字符串,并选择显示字形。
同样,如果你将输出写入文件(使用
fprintf
或流重定向),你会看到文件包含
0xE2, 0x98, 0xA0
,然后你可以选择使用文本文件查看器将UTF-8转换为显示字形。
这部分内容都很好,你可以(也应该)编写程序,只向
FILE
流写入UTF-8编码字符。
当我们想要输出字符时,问题就开始了。理论上,这应该是可行的:
printf("%ls", L"\u2620");
预期的操作是调用
wcstombs
将Unicode代码点序列转换为多字节序列。但要使用哪种多字节格式?现在UTF-8已经普及,但过去还有其他格式,如ShiftJIS、Big-5等。您必须使用
setlocale
指定多字节格式。而区域设置的详细信息是实现定义的。
这就是问题所在。Windows不支持用于一般UTF-8输出的C语言区域设置。如果尝试使用
setlocale(LC_CTYPE, ".65001");
,它将无法工作。您可以通过使用受支持的区域设置来输出某些Unicode子集。例如,使用
Japanese_Japan.932
的
MSDN示例可行,将Unicode输入输出为Shift-JIS(而非UTF-8)。
更糟糕的是,如果您使用Windows API函数
WideStringToMultiByte
,它确实接受
CP_UTF8
的“locale”。您可以使用此函数将
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运行时不会对进行任何操作;它不支持面向宽字符的流。Microsoft的系列实现实际上只输出窄字符,而不是宽字符,并且它们使用与窄printf系列相同的方法。
所以,那段代码不起作用,而来自日语wcstombs示例的代码(使用.932 CP集)输出多字节序列而不是原始宽字符。
要通过 API编写UTF-16文件,您实际上别无选择,只能使用窄字符并将其视为二进制文件。
printf("%ls", "☠")
成功了吗?格式%ls
是用于wchar_t*
的,也许你应该尝试printf("%s", "☠")
。 - Barmak Shemiraniwchar_t
- 它期望 UTF8。编译器应该能够理解wchar_t*
字符串,但非 Windows 系统可能不知道该怎么做。 - Barmak Shemirani%s
和%ls
。请说明您是否使用Microsoft实现。 - M.M