在Windows控制台中正确打印utf8字符

rsk*_*k82 16 c++ console mingw utf-8 windows-xp-sp3

这是我尝试这样做的方式:

#include <stdio.h>
#include <windows.h>
using namespace std;

int main() {
  SetConsoleOutputCP(CP_UTF8);
   //german chars won't appear
  char const* text = "aäbcdefghijklmnoöpqrsßtuüvwxyz";
  int len = MultiByteToWideChar(CP_UTF8, 0, text, -1, 0, 0);
  wchar_t *unicode_text = new wchar_t[len];
  MultiByteToWideChar(CP_UTF8, 0, text, -1, unicode_text, len);
  wprintf(L"%s", unicode_text);
}
Run Code Online (Sandbox Code Playgroud)

效果是只显示我们的ascii字符.没有显示错误.源文件以utf8编码.

那么,我在这里做错了什么?

到WouterH:

int main() {
  SetConsoleOutputCP(CP_UTF8);
  const wchar_t *unicode_text = L"aäbcdefghijklmnoöpqrsßtuüvwxyz";
  wprintf(L"%s", unicode_text);
}
Run Code Online (Sandbox Code Playgroud)
  • 这也行不通.效果是一样的.我的字体当然是Lucida Console.

第三步:

#include <stdio.h>
#define _WIN32_WINNT 0x05010300
#include <windows.h>
#define _O_U16TEXT  0x20000
#include <fcntl.h>

using namespace std;

int main() {
    _setmode(_fileno(stdout), _O_U16TEXT);
    const wchar_t *u_text = L"aäbcdefghijklmnoöpqrsßtuüvwxyz";
    wprintf(L"%s", u_text);
}
Run Code Online (Sandbox Code Playgroud)

确定,某些东西开始起作用,但输出是:a?bcdefghijklmno÷pqrs?tu?vwxyz.

bam*_*s53 16

默认情况下,Windows上的宽打印功能不处理ascii范围之外的字符.

有几种方法可以将Unicode数据传输到Windows控制台.

  • 直接使用控制台API,WriteConsoleW.你必须确保你实际写入控制台并在输出到其他东西时使用其他方法.

  • 将标准输出文件描述符的模式设置为'Unicode'模式之一,_O_U16TEXT或_O_U8TEXT.这会导致宽字符输出函数正确地将Unicode数据输出到Windows控制台.如果它们用于不代表控制台的文件描述符,则它们会使输出的字节流分别为UTF-16和UTF-8.NB在设置这些模式后,相应流上的非宽字符函数将无法使用并导致崩溃.您必须仅使用宽字符函数.

  • 如果使用正确的功能,可以通过将控制台输出代码页设置为CP_UTF8将UTF-8文本直接打印到控制台.大多数更高级别的功能,例如basic_ostream<char>::operator<<(char*)不以这种方式工作,但您可以使用较低级别的功能或实现您自己的ostream,解决标准功能所具有的问题.

第三种方法的问题是:

putc('\302'); putc('\260'); // doesn't work with CP_UTF8

puts("\302\260"); // correctly writes UTF-8 data to Windows console with CP_UTF8 
Run Code Online (Sandbox Code Playgroud)

与大多数操作系统不同,Windows上的控制台不仅仅是接受字节流的另一个文件.它是由程序创建和拥有的特殊设备,可通过其自己独特的WIN32 API访问.问题在于,当编写控制台时,API会准确地看到在使用其API时传递的数据的范围,并且在不考虑数据可能不完整的情况下发生从窄字符到宽字符的转换.当使用对控制台API的多个调用传递多字节字符时,每个单独传递的片段被视为非法编码,并被视为非法编码.

它应该很容易解决这个问题,但微软的CRT团队认为它不是他们的问题,而无论在控制台上工作的团队可能都不在乎.

您可以通过实现自己的streambuf子类来解决它,该子类处理正确转换为wchar_t.即,考虑到多字节字符的字节可以单独出现的事实,保持写入之间的转换状态(例如,std::mbstate_t).


huy*_*itw 13

另一个技巧,而不是SetConsoleOutputCP,将使用_setmodestdout:

// Includes needed for _setmode()
#include <io.h>
#include <fcntl.h>

int main() {
    _setmode(_fileno(stdout), _O_U16TEXT);  
    wchar_t * unicode_text = L"aäbcdefghijklmnoöpqrsßtuüvwxyz";
    wprintf(L"%s", unicode_text);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

别忘了删除电话 SetConsoleOutputCP(CP_UTF8);

  • 在用Windows操作大约两个小时之后,这在我的案例中起作用.谢谢! (3认同)
  • 这是我在互联网上发现的唯一一个在Windows中输出unicode文本的东西(输出希腊语).甚至不需要`system("chcp 65001")`.经过约3个小时的搜索,这项工作.谢谢!现在我还需要学习如何从文件中输出unicode utf-8文本.酷刑从未停止过. (2认同)

vla*_*vic 5

//Save As UTF8 without signature
#include<stdio.h>
#include<windows.h>
int main() {
  SetConsoleOutputCP(65001);
  const char unicode_text[]="aäbcdefghijklmnoöpqrsßtuüvwxyz";
  printf("%s\n", unicode_text);
}
Run Code Online (Sandbox Code Playgroud)

结果:
aäbcdefghijklmnoöpqrsßtuüvwxyz

  • 没用 我得到一些类似的信息:“aÃbcdefghijklmnoÃpqrsÃtutuvvxyz”。 (4认同)
  • cp65001 有错误,例如 [`putchar('\302'); putchar('\260');` 失败,但 `puts("\302\260");` 有效](http://stackoverflow.com/a/10884364/4279)。 (2认同)