wprintf无法正确输出emoji字符,但WriteConsoleW可以

r3i60tvu  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(95)

我一直在测试将Unicode文本(如表情符号)写入CLI工具的控制台。我注意到,当使用wprintf输出一个emoji时,它会打印出两个问号(�),但当我使用WriteConsoleW时,它会输出得非常好。
测试代码(使用/utf-8的MSVC和使用-municode的MinGW编译)

#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#include <Windows.h>

int wmain(int argc, const wchar_t *argv[]) {
    _setmode(_fileno(stdout), _O_U16TEXT);
    
    const wchar_t myString[] = L"😊\n";
    wprintf(L"%s", myString);
    WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), myString, wcslen(myString), NULL, NULL);
}

输出量:

jckbn6z7

jckbn6z71#

根据C语言规范

wprintf系列函数与printf类似函数的区别主要体现在两个方面:

  • 他们希望格式作为宽字符串提供,
  • 他们希望输出媒体是面向广泛的

它们在格式字符串的语义解释上没有区别。特别是,%s转换说明符对wprintf()的意义与对printf()的意义完全相同:对应的参数是指向char数组的第一个元素的指针--而不是wchar_t。如果你想打印一个 wide 字符串,那么你需要使用适当的长度修饰符,得到%ls。也就是说,

wprintf(L"%ls", myString);

请注意,您可以对常规printf执行相同的操作:

printf("%ls", myString);

.尽管您很可能会发现printfwprintf执行的代码转换存在差异。
自从在C99中引入面向宽的I/O以来,情况一直如此。

根据微软

微软的C编译器和运行时库在很多方面都是不一致的。其中之一是wprintf系列函数的行为,因为wprintf将不带宽度修饰符的%c%s转换规范分别解释为对应于wchar_twchar_t *参数。
它将%ls识别为对应于所有函数的宽字符串,并将%lc识别为对应于所有函数的wchar_t(后者仍然可能不符合标准,因为标准说%lc对应于wint_t)。它们还识别MS特定的%hc%hs,分别对应于所有功能的charchar *
| 建议:|
| --|
| 对于宽字符串和宽字符,始终使用%ls%lc转换说明符,以及所有格式化的I/O函数(常规和宽)。这就是C语言所需要的,它将与MS库一起工作。|

一般注意事项

C语言说明了哪些数据被发送到外部设备,但不说明这些设备如何处理这些数据。特定于Windows的_setmode()在这个领域运行,各种报告建议您的_setmode(_fileno(stdout), _O_U16TEXT)应该使wprintf产品在您的情况下做正确的事情。我没有足够的信息来确定发生了什么--是关于你的控制台配置的吗?关于你如何在源文件中编码数据的一些事情?在你使用的Windows或MS CRT版本中,是否有什么东西的工作方式与它工作的版本不同?你的编译器选项中是否有什么东西导致了问题?
在任何情况下,UTF-16都是一种可怕的编码。在你可以摆脱它的地方,UTF-8在几乎所有方面都更可取。常规printf()不需要任何特殊配置来发送UTF-8字符串(它无法将它们与任何其他C字符串区分开来),因此使用UTF-8编码的源代码并将控制台配置为UTF-8模式将是一种合理的尝试方法。

相关问题