在Windows控制台中输出Unicode字符串

85
嗨,我试图使用iostreams将Unicode字符串输出到控制台,但失败了。
我找到了这个链接:在C++控制台应用程序中使用Unicode字体,这段代码可以工作。
SetConsoleOutputCP(CP_UTF8);
wchar_t s[] = L"èéøÞǽлљΣæča";
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize]; 
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
wprintf(L"%S", m);

然而,我没有找到任何使用iostreams正确输出unicode的方法。有什么建议吗?
这种方法不起作用:
SetConsoleOutputCP(CP_UTF8);
utf8_locale = locale(old_locale,new boost::program_options::detail::utf8_codecvt_facet());
wcout.imbue(utf8_locale);
wcout << L"¡Hola!" << endl;

编辑 我找不到其他解决办法,只能将这段代码包装在一个流中。 希望有人有更好的想法。

//Unicode output for a Windows console 
ostream &operator-(ostream &stream, const wchar_t *s) 
{ 
    int bufSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
    char *buf = new char[bufSize];
    WideCharToMultiByte(CP_UTF8, 0, s, -1, buf, bufSize, NULL, NULL);
    wprintf(L"%S", buf);
    delete[] buf; 
    return stream; 
} 

ostream &operator-(ostream &stream, const wstring &s) 
{ 
    stream - s.c_str();
    return stream; 
} 

你能具体说明它是如何失败的吗?你是否收到了乱码/错误字符或其他什么?你尝试过捕获STDOUT并验证正确的字节是否被发送但未显示吗? - Goyuix
1
它显示占位符而不是字符。我没有深入研究它。我唯一能说的是,由于某种原因,发送到wcout或cout的相同字符串会变得混乱,而wprintf则可以正常显示它。 - Andrew
只有一些Unicode字符可以在Win32控制台中正确显示。控制台不支持过于复杂或带有影响其大小的组合标记的字符。尝试使用WriteConsoleW - 如果它无法正常工作,则不可能实现。 - user541686
17个回答

0

0

最近我想从Python流式传输Unicode到Windows控制台,这里是我需要的最小设置:

  • 您应该将控制台字体设置为覆盖Unicode符号的字体之一。选择不多:控制台属性>字体>Lucida Console
  • 您应该更改当前的控制台代码页:在控制台中运行chcp 65001或使用C++代码中的对应方法
  • 使用WriteConsoleW向控制台写入

阅读一篇关于Java在Windows控制台上使用Unicode的有趣文章。

此外,在这种情况下,您不能将内容写入默认sys.stdout,您需要使用os.write(1,binarystring)或直接调用WriteConsoleW的包装器替换它。似乎在C++中您也需要这样做。


4
需要设置字体,这部分是正确的,因为Windows不默认使用适用于广泛Unicode字符范围的字体是设计不良。但是你回答的下一部分是错误的。您不需要将代码页设置为UTF-8/65001并调用WriteConsoleW。您只需要执行其中之一。如果您将调用WriteConsoleA并传入8位字符串(包括UTF-8),则设置代码页,但只需调用WriteConsoleW完全绕过了代码页并需要UTF-16(宽字符)。但根据我的经验,将控制台设置为65001会出现许多错误。 - hippietrail
@hippietrail:我不确定在不将代码页更改为65001的情况下使用WriteConsoleW进行编写是否可行,但仅设置为65001是不够的。至少对于Python脚本的Unicode输出而言是如此。 - newtover

0

在Win10下使用UK区域设置从VS2017运行控制台应用程序需要:

  1. 设置VS2017工具 > 环境 > 字体和颜色 > 字体:例如'Lucida'
  2. 使用编码“Unicode(带签名的UTF-8) - 代码页650001”保存C++源文件,以便您可以键入带重音符号的字符文字L"âéïôù"而不会出现编译器警告,但避免双字节字符
  3. 使用配置属性 > 常规 > 字符集 > “使用多字节..”和配置属性 > C/C++ > 所有选项 > 其他选项 > “/utf-8”标志进行编译
  4. #include <iostream>, <io.h>, 和 <fcntl.h>
  5. 在应用程序开始时执行一个晦涩的'_setmode(_fileno(stdout), _O_WTEXT);'
  6. 忘记'cout <<... ;',只使用'wcout << ... ;'

备忘录:在Win7上的VS2015需要'SetConsoleOutputCP(65001);'并允许通过wcout和cout混合输出。


0

在Win10上使用VS2019测试UNICODE控制台应用程序时,发现以下测试结果:西班牙语和日语:

如果您只是wprintf一个字符串,那么对于西班牙语(未测试日语,但肯定不起作用),您会得到错误的字符。似乎默认的“C”区域设置是ASCII(PC的传统扩展ASCII排序表)。

使用:setlocale(LC_ALL, "");将代码页设置为CP1252,当使用西班牙语(墨西哥) Windows语言设置时,输出正常(lucida控制台字体)。然而,使用日语 Windows语言时,输出被抑制(意味着这些字符没有输出,普通的拉丁字符输出)。

使用:'_setmode(_fileno(stdout), _O_U16TEXT);'所有输出都能正确工作。但是,所有输出都是16位的,因此重定向到文件会输出16位字符。

使用:printfUTF-8文本输出与SetConsoleOutputCP(CP_UTF8)也可以工作(但如果您在setlocale(LC_ALL, "");之后设置它,则无法工作-我必须删除该设置才能使输出正常工作)。

字体:对于亚洲字符,请使用MS Mincho,对于其他字符,您可以使用Lucida Console。


0

在Windows控制台中正确显示西欧字符

简而言之:

  1. 使用chcp查找适合您的代码页。在我的情况下,它是chcp 28591用于西欧。
  2. 可选地将其设置为默认值:REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591

发现的历史

我遇到了类似的问题,与Java有关。这只是一个外观问题,因为它涉及发送到控制台的日志行;但仍然很烦人。

我们的Java应用程序输出应该是UTF-8格式,在eclipse的控制台中正确显示。但在Windows控制台中,它只显示ASCII框图字符:Inicializaci├│nart├¡culos而不是Inicializaciónartículos

我偶然发现了相关问题,混合了一些答案来找到适合我的解决方法。解决方法是更改控制台使用的代码页使用支持UNICODE的字体(例如consolaslucida console)。您可以在Windows控制台的系统菜单中选择字体:

通过以下任一方式启动控制台:
  • Win + R 键,然后键入 cmd 并按下 回车 键。
  • 按下 Win 键并输入 cmd ,然后按下 回车 键。
通过以下任一方式打开系统菜单:
  • 点击左上角图标
  • Alt + Space 键组合
然后选择“默认值”以更改所有后续控制台窗口的行为,点击“字体”选项卡,选择 Consolas 或 Lucida console ,最后点击 OK。
关于代码页,对于一次性情况,您可以使用命令 chcp 完成它,然后必须调查哪个代码页适用于您的字符集。几个答案建议使用 UTF-8 代码页,即 65001,但是该代码页对我的西班牙语字符无效。

另一个答案建议使用批处理脚本从列表中交互式地选择所需的代码页。在那里,我找到了我需要的ISO-8859-1的代码页:28591。因此,您可以执行以下操作:

chcp 28591

在每次执行应用程序之前,您可能需要检查哪个代码页适合您在Code Page Identifiers MSDN page中查看。

另一个答案指出如何将所选代码页保留为Windows控制台的默认设置。这涉及更改注册表,因此请注意,使用此解决方案可能会使您的计算机变得不稳定。

REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591

这将在 HKCU\Console 注册表项中创建 CodePage 值,其中包含 28591 数据。 这对我有效。

请注意,HKCU("HKEY_CURRENT_USER")仅适用于当前用户。 如果您想要更改所有计算机用户的设置,则需要使用 regedit 实用程序,并查找/创建相应的 Console 键(可能需要在 HKEY_USERS\.DEFAULT 中创建 Console 键)。


0

解决方案1:使用WCHAR

一种始终有效的方法是在所有地方使用宽字符,例如:

const wchar_t* str = L"你好\n";
DWORD nwritten = 0;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, 3, &nwritten, NULL);

Unicode是一种语言中立的编码方式。您可以使用任何语言而不会有编码问题。您想使用UTF-8?好的。先使用MultiByteToWideChar将其转换为宽字符字符串。

在继续阅读下面的其他解决方案之前,请注意此解决方案具有独特优势:它不依赖于系统或用户的区域设置。

解决方案2:正确设置系统区域设置和用户区域设置,它们应该相同。

我假设Windows的UTF-8语言环境还没有出现。然后您需要知道要使用哪种语言(中文、法语等),并更改系统设置以匹配它。有系统级别的设置: 更改系统代码页

还有用户级别的设置: 输入图像描述

请将它们都设置为相同的语言。

然后,在您的程序中,在主函数中插入“setlocale(LC_ALL, "");”。这是一个通用规则,无论您使用哪个操作系统,当您想使用标准库来处理ASCII以外的字符集时,您都应该有这行代码。否则,区域设置默认为“C”,它仅包含ASCII。然后您可以开始使用std::wcout和像fputws这样的C函数。


0
在我的情况下,我正在读取UTF-8文件并打印到Console中,我发现wifstream非常好用,即使在Visual Studio调试器中以正确的方式显示UTF-8单词(我正在阅读繁体中文),来源于this post
#include <sstream>
#include <fstream>
#include <codecvt>

std::wstring readFile(const char* filename)
{
    std::wifstream wif(filename);
    wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
    std::wstringstream wss;
    wss << wif.rdbuf();
    return wss.str();
}

//  usage
std::wstring wstr2;
wstr2 = readFile("C:\\yourUtf8File.txt");
wcout << wstr2;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接