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

Output Unicode strings in Windows console

提问人:Andrew 提问时间:3/22/2010 最后编辑:vitautAndrew 更新时间:6/9/2023 访问量:100306

问:

嗨,我试图将 unicode 字符串输出到带有 iostreams 的控制台,但失败了。

我发现这个:在 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; 
} 
C++ Unicode IOSTREAM Windows 控制台

评论

0赞 Goyuix 3/31/2010
你能澄清一下它是如何失败的吗?你是乱码/错误的字符还是其他什么?您是否尝试过捕获 STDOUT 并验证是否发送了正确的字节,但可能没有显示?
1赞 Andrew 3/31/2010
它显示占位符而不是字符。我没有深入研究它。我唯一能说的是,出于某种原因,发送到 wcout 或 cout 的同一字符串会发疯,而 wprintf 毫无问题地显示它。
0赞 user541686 1/29/2012
Win32 控制台中只能正确显示某些 Unicode 字符。控制台不支持过于复杂的字符,也不支持具有影响其大小的组合标记的字符。尝试使用 WriteConsoleW - 如果它不起作用,那么这是不可能的。

答:

0赞 call me Steve 3/31/2010 #1

我不认为有一个简单的答案。查看控制台代码页SetConsoleCP 函数,似乎需要为要输出的字符集设置适当的代码页。

2赞 Puppy 4/1/2010 #2

wcout 必须将区域设置设置为与 CRT 不同的区域设置。以下是修复方法:

int _tmain(int argc, _TCHAR* argv[])
{
    char* locale = setlocale(LC_ALL, "English"); // Get the CRT's current locale.
    std::locale lollocale(locale);
    setlocale(LC_ALL, locale); // Restore the CRT.
    std::wcout.imbue(lollocale); // Now set the std::wcout to have the locale that we got from the CRT.
    std::wcout << L"¡Hola!";
    std::cin.get();
    return 0;
}

我刚刚测试了它,它在这里显示字符串绝对没问题。

评论

1赞 Andrew 4/1/2010
感谢您的新想法,它适用于此字符串,但对于更复杂的东西,例如“¡Hola!αβγ ambulō привет :)”
0赞 Puppy 4/1/2010
该字符串在 wprintf 上也不起作用,只是完全空白。WCOUT 至少让一些角色正确。你能仔细检查一下 wprintf 是否正确了这个字符串吗?
0赞 Andrew 4/1/2010
是的,如果您为控制台选择正确的字体并使用 cmd 启动它.exe它就可以工作
4赞 Cheers and hth. - Alf 6/19/2014
-1 表示语言环境 idea + 使用 和 .修复区域设置仅支持该区域设置的 Windows ANSI 编码中的字符。它不支持常规 Unicode 输出(甚至不支持 UCS2)。_tmain_TCHAR
0赞 Adrian 11/16/2018
似乎有效。不幸的是,现在我的数字有分组(数千个)分隔符。:(
0赞 newtover 4/1/2010 #3

最近,我想将 unicode 从 Python 流式传输到 Windows 控制台,这是我需要做的最低限度:

  • 应将控制台字体设置为覆盖 unicode 符号的字体。没有广泛的选择: Zoo 属性 > Principle > Lucida Console
  • 您应该更改当前的控制台代码页:在控制台中运行或使用 C++ 代码中的相应方法chcp 65001
  • 使用 WriteConsoleW 写入控制台

Windows 控制台上浏览有关 java unicode 的相关文章

此外,在 Python 中,在这种情况下,您不能写入默认的 sys.stdout,您需要使用 os.write(1, binarystring) 或直接调用 WriteConsoleW 周围的包装器来替换它。似乎在 C++ 中,您需要执行相同的操作。

评论

4赞 hippietrail 2/17/2011
您确实需要设置字体,这部分是正确的,并且 Windows 的设计很差,不能默认为适用于适当范围的 Unicode 字符的字体。但是,您答案的下一部分是错误的。您无需将代码页设置为 UTF-8/65001 并调用 WriteConsoleW。你需要做一个或另一个。如果要调用 WriteConsoleA 并传入包括 UTF-8 在内的 8 位字符串,请设置代码页,但仅调用 WriteConsoleW 完全绕过代码页并需要 UTF-16(宽字符)。然而,根据我的经验,将控制台设置为 65001 是相当错误的。
0赞 newtover 3/25/2011
@hippietrail:我不确定在不将代码页更改为 65001 的情况下使用 WriteConsoleW 编写,但不幸的是,仅设置为 65001 是不够的。至少对于 Python 脚本的 unicode 输出。
0赞 Afriza N. Arief 4/5/2010 #4

首先,对不起,我可能没有所需的字体,所以我还不能测试它。

这里看起来有点腥

// the following is said to be working
SetConsoleOutputCP(CP_UTF8); // output is in 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); // <-- upper case %S in wprintf() is used for MultiByte/utf-8
                   //     lower case %s in wprintf() is used for WideChar
printf("%s", m); // <-- does this work as well? try it to verify my assumption

// the following is said to have problem
SetConsoleOutputCP(CP_UTF8);
utf8_locale = locale(old_locale,
                     new boost::program_options::detail::utf8_codecvt_facet());
wcout.imbue(utf8_locale);
wcout << L"¡Hola!" << endl; // <-- you are passing wide char.
// have you tried passing the multibyte equivalent by converting to utf8 first?
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);
cout << m << endl;

怎么样

// without setting locale to UTF8, you pass WideChars
wcout << L"¡Hola!" << endl;
// set locale to UTF8 and use cout
SetConsoleOutputCP(CP_UTF8);
cout << utf8_encoded_by_converting_using_WideCharToMultiByte << endl;

评论

0赞 Andrew 4/5/2010
这是有趣的部分。我试过了,我很惊讶它不起作用,但无论如何都要感谢
109赞 DuckMaestro 1/29/2012 #5

我已经使用 Visual Studio 2010 在这里验证了一个解决方案。通过这篇 MSDN 文章MSDN 博客文章。诀窍是对 ._setmode(..., _O_U16TEXT)

溶液:

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

int wmain(int argc, wchar_t* argv[])
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    std::wcout << L"Testing unicode -- English -- Ελληνικά -- Español." << std::endl;
}

截图:

Unicode in console

评论

4赞 Billy ONeal 4/24/2012
+1 并删除了我的答案。这是我们为 Instalog 选择的方法。
8赞 sarat 4/11/2013
它仍然没有在我的控制台中显示日语字符。
2赞 Cheers and hth. - Alf 6/19/2014
+1 表示有效的修复程序,应该注意,这是一个特定于 C++ 的 VIsual 解决方案:它不一定适用于 g++。
13赞 Roger Dahl 8/27/2014
Doesn't work when you also have std::cout's从 cplusplus.com:程序不应将 wcout 上的输出操作与 cout 上的输出操作(或 stdout 上的其他窄向输出操作)混合:一旦在任一上执行了输出操作,标准输出流就会获得一个方向(窄或宽),该方向只能通过在 stdout 上调用 freopen 来安全地更改。
2赞 Roi Danton 1/15/2019
在处理答案中的字符串时,它不适用于较长的字符,例如 或 。看起来,默认控制台字体不支持这些字体(这可能取决于 Windows 中的区域设置)。L"안녕하세요."L"你好!"
1赞 Henrik Haftmann 7/17/2012 #6

SetConsoleCP() 和 chcp 不一样!

以此程序片段为例:

SetConsoleCP(65001)  // 65001 = UTF-8
static const char s[]="tränenüberströmt™\n";
DWORD slen=lstrlen(s);
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),s,slen,&slen,NULL);

源代码必须保存为不带 BOM(字节顺序标记;签名)。然后,Microsoft 编译器 cl.exe 按原样采用 UTF-8 字符串。
如果此代码与 BOM 一起保存,则 cl.exe 将字符串转码为 ANSI(即 CP1252),这与 CP65001 (= UTF-8) 不匹配。

将显示字体更改为 Lucidia 控制台,否则 UTF-8 输出将根本不起作用。

  • 类型:chcp
  • 答:850
  • 类型:test.exe
  • 答:tr├ñnen├╝berstr├ÂmtÔäó
  • 类型:chcp
  • 答: - 此设置已通过 SetConsoleCP() 更改,但没有有用的效果。65001
  • 类型:chcp 65001
  • 类型:test.exe
  • 答: - 现在一切正常。tränenüberströmt™

与以下设备测试: German Windows XP SP3

评论

1赞 phuclv 6/6/2014
您可以使用字符常量,例如使字符串工作,而不考虑源编码\x45
2赞 Cheers and hth. - Alf 6/19/2014
-1 欺骗编译器的不好建议,导致编译错误。
0赞 user4815162342 11/16/2014
您确实希望使用转义,以免依赖于编辑器如何保存源代码中的非 ASCII 字符并由编译器解释。例如,答案中的 UTF-8 字符串可以移植地写成 。"tr\xc3\xa4nen\xc3\xbcberstr\xc3\xb6mt\xe2\x84\xa2"
2赞 rdb 11/22/2017
SetConsoleCP只影响输入,所以它在你的示例中不起作用也就不足为奇了。它控制输出编码。SetConsoleOutputCP
0赞 manuelvigarcia 9/20/2017 #7

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

长话短说:

  1. 用于查找适合您的代码页。就我而言,它是针对西欧的。chcpchcp 28591
  2. (可选)将其设为默认值:REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591

发现的历史

我在 Java 上遇到了类似的问题。它只是装饰性的,因为它涉及发送到控制台的日志行;但它仍然很烦人。

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

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

  1. 通过以下任一方式启动控制台
    • Win + R然后键入并按键。cmdReturn
    • 按键并键入,然后按键。Wincmdreturn
  2. 通过以下任意一项打开系统菜单
    • 单击左上角图标
    • 点击组合键Alt + Space
  3. 然后选择“默认”以更改所有后续控制台窗口的行为
  4. 点击“字体”选项卡
  5. 选择或ConsolasLucida console
  6. 点击OK

关于代码页,对于一次性情况,您可以使用命令完成它,然后您必须调查哪个代码页对于您的字符集是正确的。几个答案建议使用 UTF-8 代码页,即 65001,但该代码页不适用于我的西班牙语字符。chcp

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

chcp 28591

在每次执行应用程序之前。您可以在代码页标识符 MSDN 页中检查哪个代码页适合您。

另一个答案指示了如何将所选代码页保留为 Windows 控制台的默认代码页。它涉及更改注册表,因此请考虑自己警告,您可能会使用此解决方案使计算机变砖。

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

这将使用 HKCU\Console 注册表项中的数据创建值。这确实对我有用。CodePage28591

请注意,HKCU(“HKEY_CURRENT_USER”)仅适用于当前用户。如果要为该计算机中的所有用户更改它,则需要使用该实用程序并查找/创建相应的密钥(可能您必须在内部创建一个密钥regeditConsoleConsoleHKEY_USERS\.DEFAULT)

0赞 Victor Gubin 3/2/2018 #8

mswcrt 和 io 流存在一些问题。

  1. 技巧 _setmode(_fileno(stdout), _O_U16TEXT);仅适用于 MS VC++,不适用于 MinGW-GCC。此外,有时它会导致崩溃,具体取决于 Windows 配置。
  2. SetConsoleCP(65001) 用于 UTF-8。在许多多字节字符方案中可能会失败,但对于 UTF-16LE 始终可以
  3. 您需要在应用程序退出时还原预览控制台代码页。

Windows 控制台支持 UNICODE 和 UTF-16LE 模式下的 ReadConsole 和 WriteConsole 函数。背景效果 - 在这种情况下,管道将不起作用。即 myapp.exe >> ret.log 将文件设置为 0 字节 ret.log。如果您对此感到满意,您可以尝试我的库,如下所示。

const char* umessage = "Hello!\nПривет!\nПривіт!\nΧαιρετίσματα!\nHelló!\nHallå!\n";

...
#include <console.hpp>
#include <ios>
...

std::ostream& cout = io::console::out_stream();
cout << umessage
<< 1234567890ull << '\n'
<< 123456.78e+09 << '\n'
<< 12356.789e+10L << '\n'
<< std::hex << 0xCAFEBABE
<< std::endl;

库会自动将您的 UTF-8 转换为 UTF-16LE,并使用 WriteConsole 将其写入控制台。以及错误和输入流。 图书馆的另一个好处 - 颜色。

示例应用上的链接:https://github.com/incoder1/IO/tree/master/examples/iostreams

图书馆主页:https://github.com/incoder1/IO

截图:

11赞 David 3/26/2018 #9

Unicode Hello World 中文

这里有一个中文的Hello World。实际上它只是“你好”。我在 Windows 10 上测试了它,但我认为它可能从 Windows Vista 开始工作。 在Windows Vista之前,如果您想要一个编程解决方案,而不是配置控制台/注册表等,这将是困难的。如果您真的需要在 Windows 7 上执行此操作,也许可以看看这里: 更改控制台字体 Windows 7

我不想声称这是唯一的解决方案,但这就是对我有用的方法。

大纲

  1. Unicode 项目设置
  2. 将控制台代码页设置为 unicode
  3. 查找并使用支持要显示的字符的字体
  4. 使用要显示的语言的区域设置
  5. 使用宽字符输出,即std::wcout

1 项目设置

我正在使用 Visual Studio 2017 CE。我创建了一个空白的控制台应用。默认设置没问题。但是,如果您遇到问题或使用其他 IDE,您可能需要检查以下内容:

在项目属性中,找到配置属性 -> 常规 ->项目默认值 ->字符集。它应该是“使用 Unicode 字符集”而不是“多字节”。 这将为您定义和预处理器宏。_UNICODEUNICODE

int wmain(int argc, wchar_t* argv[])

另外,我认为我们应该使用函数而不是.它们都可以工作,但在 unicode 环境中可能更方便。wmainmainwmain

此外,我的源文件是 UTF-16-LE 编码的,这似乎是 Visual Studio 2017 中的默认设置。

2 控制台代码页

这是很明显的。我们需要控制台中的 unicode 代码页。 如果要检查默认代码页,只需打开控制台并键入任何参数即可。 我们必须将其更改为 65001,即 UTF-8 代码页。Windows 代码页标识符该代码页有一个预处理器宏:。 我需要同时设置输入和输出代码页。当我省略任何一个时,输出不正确。chcpCP_UTF8

SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);

您可能还需要检查这些函数的布尔返回值。

3 选择字体

到目前为止,我还没有找到支持每个字符的控制台字体。所以我不得不选择一个。 如果要输出部分仅以一种字体提供,部分以另一种字体提供的字符,那么我相信不可能找到解决方案。只有当有一种字体支持每个字符时,才可能。但我也没有研究如何安装字体。

我认为不可能在同一控制台窗口中同时使用两种不同的字体。

如何找到兼容的字体? 打开控制台,单击窗口左上角的图标转到控制台窗口的属性。转到“字体”选项卡,选择一种字体,然后单击“确定”。然后尝试在控制台窗口中输入您的字符。重复此操作,直到找到可以使用的字体。然后记下字体的名称。

此外,您还可以在属性窗口中更改字体的大小。 如果找到满意的尺寸,请记下属性窗口中“所选字体”部分中显示的大小值。它将以像素为单位显示宽度和高度。

若要以编程方式实际设置字体,请使用:

CONSOLE_FONT_INFOEX fontInfo;
// ... configure fontInfo
SetCurrentConsoleFontEx(hConsole, false, &fontInfo);

有关详细信息,请参阅本答案末尾的示例。 或者在精美的手册中查找它:SetCurrentConsoleFont。 此函数仅从 Windows Vista 开始存在。

4 设置区域设置

您需要将区域设置设置为要打印的字符的语言的区域设置。

char* a = setlocale(LC_ALL, "chinese");

返回值很有意思。它将包含一个字符串来准确描述所选择的区域设置。 试一试吧:-) 我用 和 进行了测试。 更多信息: setlocalechinesegerman

5 使用宽字符输出

这里没什么好说的。 如果要输出宽字符,请使用以下示例:

std::wcout << L"你好" << std::endl;

哦,别忘了宽字符的前缀! 如果在源文件中键入这样的文字 Unicode 字符,则源文件必须是 unicode 编码的。就像 Visual Studio 中的默认值一样,是 UTF-16-LE。或者使用记事本++并将编码设置为.LUCS-2 LE BOM

最后,我把它们放在一起作为一个例子:

#include <Windows.h>
#include <iostream>
#include <io.h>
#include <fcntl.h>
#include <locale.h>
#include <wincon.h>

int wmain(int argc, wchar_t* argv[])
{
    SetConsoleTitle(L"My Console Window - 你好");
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    char* a = setlocale(LC_ALL, "chinese");
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);

    CONSOLE_FONT_INFOEX fontInfo;
    fontInfo.cbSize = sizeof(fontInfo);
    fontInfo.FontFamily = 54;
    fontInfo.FontWeight = 400;
    fontInfo.nFont = 0;
    const wchar_t myFont[] = L"KaiTi";
    fontInfo.dwFontSize = { 18, 41 };
    std::copy(myFont, myFont + (sizeof(myFont) / sizeof(wchar_t)), fontInfo.FaceName);
    
    SetCurrentConsoleFontEx(hConsole, false, &fontInfo);

    std::wcout << L"Hello World!" << std::endl;
    std::wcout << L"你好!" << std::endl;
    return 0;
}

干杯!

于 2021-11-20 编辑

也许您也可以尝试使用新的 Windows 终端。它似乎开箱即用地打印 unicode。您仍然需要在设置中配置支持您的字符的字体。它由 Microsoft 在 github 上作为开源开发,您也可以从 Microsoft Store 安装它。我在 Windows 10 上成功尝试了这个。

评论

0赞 zezba9000 3/31/2019
这对我不起作用。将 C 与 “wprintf(L”你好“)一起使用;”
0赞 zezba9000 3/31/2019
将 std::copy 更改为“memcpy(fontInfo.FaceName, myFont, (sizeof(myFont)));”,它在 C++ 中适用于 .cpp 文件,但如果我使用 .c 文件编译 C,则不能。
1赞 zezba9000 3/31/2019
Nvr 头脑,它的工作。只需要确保您的源文件是正确的 UTF-8 编码(带签名)。
0赞 Luan Vitor 9/28/2021
SetCurrentConsoleFontEx函数处于支持模式,不再鼓励在新的命令行应用中使用。
0赞 Joma 2/23/2019 #10

默认编码:

  • Windows UTF-16 格式。
  • Linux UTF-8 格式。
  • MacOS UTF-8 格式。

我的解决方案步骤,包括 null chars \0(避免截断)。在不使用 windows.h 标头上的函数的情况下:

  1. 添加宏以检测平台。
#if defined (_WIN32) 
#define WINDOWSLIB 1

#elif defined (__ANDROID__) || defined(ANDROID)//Android
#define ANDROIDLIB 1

#elif defined (__APPLE__)//iOS, Mac OS
#define MACOSLIB 1

#elif defined (__LINUX__) || defined(__gnu_linux__) || defined(__linux__)//_Ubuntu - Fedora - Centos - RedHat
#define LINUXLIB 1
#endif
  1. 创建将 std::w字符串转换为 std::string 的函数,反之亦然。
#include <locale>
#include <iostream>
#include <string>
#ifdef WINDOWSLIB
#include <Windows.h>
#endif

using namespace std::literals::string_literals;

// Convert std::wstring to std::string
std::string WidestringToString(const std::wstring& wstr, const std::string& locale)
{
    if (wstr.empty())
    {
        return std::string();
    }
    size_t pos;
    size_t begin = 0;
    std::string ret;
    size_t  size;
#ifdef WINDOWSLIB
    _locale_t lc = _create_locale(LC_ALL, locale.c_str());
    pos = wstr.find(static_cast<wchar_t>(0), begin);
    while (pos != std::wstring::npos && begin < wstr.length())
    {
        std::wstring segment = std::wstring(&wstr[begin], pos - begin);
        _wcstombs_s_l(&size, nullptr, 0, &segment[0], _TRUNCATE, lc);
        std::string converted = std::string(size, 0);
        _wcstombs_s_l(&size, &converted[0], size, &segment[0], _TRUNCATE, lc);
        ret.append(converted);
        begin = pos + 1;
        pos = wstr.find(static_cast<wchar_t>(0), begin);
    }
    if (begin <= wstr.length()) {
        std::wstring segment = std::wstring(&wstr[begin], wstr.length() - begin);
        _wcstombs_s_l(&size, nullptr, 0, &segment[0], _TRUNCATE, lc);
        std::string converted = std::string(size, 0);
        _wcstombs_s_l(&size, &converted[0], size, &segment[0], _TRUNCATE, lc);
        converted.resize(size - 1);
        ret.append(converted);
    }
    _free_locale(lc);
#elif defined LINUXLIB
    std::string currentLocale = setlocale(LC_ALL, nullptr);
    setlocale(LC_ALL, locale.c_str());
    pos = wstr.find(static_cast<wchar_t>(0), begin);
    while (pos != std::wstring::npos && begin < wstr.length())
    {
        std::wstring segment = std::wstring(&wstr[begin], pos - begin);
        size = wcstombs(nullptr, segment.c_str(), 0);
        std::string converted = std::string(size, 0);
        wcstombs(&converted[0], segment.c_str(), converted.size());
        ret.append(converted);
        ret.append({ 0 });
        begin = pos + 1;
        pos = wstr.find(static_cast<wchar_t>(0), begin);
    }
    if (begin <= wstr.length()) {
        std::wstring segment = std::wstring(&wstr[begin], wstr.length() - begin);
        size = wcstombs(nullptr, segment.c_str(), 0);
        std::string converted = std::string(size, 0);
        wcstombs(&converted[0], segment.c_str(), converted.size());
        ret.append(converted);
    }
    setlocale(LC_ALL, currentLocale.c_str());
#elif defined MACOSLIB
#endif

    return ret;
}

// Convert std::string to std::wstring
std::wstring StringToWideString(const std::string& str, const std::string& locale)
{
    if (str.empty())
    {
        return std::wstring();
    }

    size_t pos;
    size_t begin = 0;
    std::wstring ret;
    size_t  size;

#ifdef WINDOWSLIB
    _locale_t lc = _create_locale(LC_ALL, locale.c_str());
    pos = str.find(static_cast<char>(0), begin);
    while (pos != std::string::npos) {
        std::string segment = std::string(&str[begin], pos - begin);
        std::wstring converted = std::wstring(segment.size() + 1, 0);
        _mbstowcs_s_l(&size, &converted[0], converted.size(), &segment[0], _TRUNCATE, lc);
        converted.resize(size - 1);
        ret.append(converted);
        ret.append({ 0 });
        begin = pos + 1;
        pos = str.find(static_cast<char>(0), begin);
    }
    if (begin < str.length()) {
        std::string segment = std::string(&str[begin], str.length() - begin);
        std::wstring converted = std::wstring(segment.size() + 1, 0);
        _mbstowcs_s_l(&size, &converted[0], converted.size(), &segment[0], _TRUNCATE, lc);
        converted.resize(size - 1);
        ret.append(converted);
    }
    _free_locale(lc);
#elif defined LINUXLIB
    std::string currentLocale = setlocale(LC_ALL, nullptr);
    setlocale(LC_ALL, locale.c_str());
    pos = str.find(static_cast<char>(0), begin);
    while (pos != std::string::npos) {
        std::string segment = std::string(&str[begin], pos - begin);
        std::wstring converted = std::wstring(segment.size(), 0);
        size = mbstowcs(&converted[0], &segment[0], converted.size());
        converted.resize(size);
        ret.append(converted);
        ret.append({ 0 });
        begin = pos + 1;
        pos = str.find(static_cast<char>(0), begin);
    }
    if (begin < str.length()) {
        std::string segment = std::string(&str[begin], str.length() - begin);
        std::wstring converted = std::wstring(segment.size(), 0);
        size = mbstowcs(&converted[0], &segment[0], converted.size());
        converted.resize(size);
        ret.append(converted);
    }
    setlocale(LC_ALL, currentLocale.c_str());
#elif defined MACOSLIB
#endif

    return ret;
}
  1. 打印 std::string。 检查 RawString 后缀

Linux 代码。使用 std::cout 直接打印 std::string。
如果您有 std::wstring.
1. 转换为 std::string。
2. 使用 std::cout 打印。

std::wstring x = L"\0\001日本ABC\0DE\0F\0G🐶\0"s;
std::string result = WidestringToString(x, "en_US.UTF-8");
std::cout << "RESULT=" << result << std::endl;
std::cout << "RESULT_SIZE=" << result.size() << std::endl;

在 Windows 上,如果您需要打印 unicode。我们需要使用 WriteConsole 从 std::wstring 或 std::string 打印 unicode 字符。

void WriteUnicodeLine(const std::string& s)
{
#ifdef WINDOWSLIB
    WriteUnicode(s);
    std::cout << std::endl;
#elif defined LINUXLIB
    std::cout << s << std::endl;
#elif defined MACOSLIB
#endif
}

void WriteUnicode(const std::string& s)
{

#ifdef WINDOWSLIB
    std::wstring unicode = Insane::String::Strings::StringToWideString(s);
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), static_cast<DWORD>(unicode.length()), nullptr, nullptr);
#elif defined LINUXLIB
    std::cout << s;
#elif defined MACOSLIB
#endif


}

void WriteUnicodeLineW(const std::wstring& ws)
{

#ifdef WINDOWSLIB
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), static_cast<DWORD>(ws.length()), nullptr, nullptr);
    std::cout << std::endl;
#elif defined LINUXLIB
    std::cout << String::Strings::WidestringToString(ws)<<std::endl;
#elif defined MACOSLIB
#endif


}

void WriteUnicodeW(const std::wstring& ws)
{

#ifdef WINDOWSLIB
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), static_cast<DWORD>(ws.length()), nullptr, nullptr);
#elif defined LINUXLIB
    std::cout << String::Strings::WidestringToString(ws);
#elif defined MACOSLIB
#endif

}

Windows 代码。使用 WriteLineUnicode 或 WriteUnicode 函数。相同的代码可用于 Linux。

std::wstring x = L"\0\001日本ABC\0DE\0F\0G🐶\0"s;
std::string result = WidestringToString(x, "en_US.UTF-8");
WriteLineUnicode(u8"RESULT" + result);
WriteLineUnicode(u8"RESULT_SIZE" + std::to_string(result.size()));

最后在 Windows 上。您需要在控制台中对 unicode 字符提供强大而完整的支持。我推荐 ConEmu 并设置为 Windows 上的默认终端

在 Microsoft Visual Studio 和 Jetbrains Clion 上进行测试。

  • 在带有 VC++ 的 Microsoft Visual Studio 2017 上测试;标准=C++17。(Windows 项目)
  • 在带有 g++ 的 Microsoft Visual Studio 2017 上测试;标准=C++17。(Linux 项目)
  • 在 Jetbrains Clion 2018.3 上使用 g++ 进行测试;标准=C++17。(Linux 工具链/远程)

质量保证

问。为什么不使用标头函数和类?
A. 弃用 已删除或弃用的功能不可能在 VC++ 上构建,但在 g++ 上没有问题。我更喜欢 0 个警告和头痛。
<codecvt>

Q. Windows 上的 wstring 是 interchan。
A. 弃用 已删除或弃用的功能不可能在 VC++ 上构建,但在 g++ 上没有问题。我更喜欢 0 个警告和头痛。

Q. std ::wstring 是跨平台的吗?
答:不可以。std::wstring 使用wchar_t元素。在 Windows 上,wchar_t大小为 2 个字节,每个字符以 UTF-16 单位存储,如果字符大于 U+FFFF,则该字符以两个 UTF-16 单位(2 个wchar_t元素)表示,称为代理项对。在 Linux 上wchar_t大小为 4 个字节,每个字符存储在一个wchar_t元素中,不需要代理项对。检查 UNIX、Linux 和 Windows 上的标准数据类型

Q. std ::string 是跨平台的吗?
答:是的。std::string 使用 char 元素。保证 char 类型在所有编译器中的字节大小相同。char 类型大小为 1 字节。检查 UNIX、Linux 和 Windows 上的标准数据类型

19赞 vitaut 12/28/2020 #11

在 C++23 中,您将能够使用 std::p rint 便携式打印 Unicode 文本:

import std;

int main() {
  std::print("Шчучыншчына");
}

输出:

Шчучыншчына

这需要使用 MSVC 中的编译器选项进行编译。/utf-8

在它可用之前,您可以使用开源的 {fmt} 库,是基于。例如:std::print

#include <fmt/core.h>

int main() {
  fmt::print("Шчучыншчына");
}

我不建议使用,因为它是不可移植的,如果没有额外的努力,它甚至无法在 Windows 上运行,例如:wcout

std::wcout << L"èéøÞǽлљΣæča";

将打印:

├и├й├╕├Ю╟╜╨╗╤Щ╬г├ж─Нa

在西里尔语 Windows(ACP 1251,控制台 CP 866)中。

免责声明:我是 {fmt} 和 C++23 的作者。std::print

0赞 Bernard Hauzeur 3/17/2021 #12

在具有英国区域设置的 Win10 下从 VS2017 运行控制台应用程序需要我:

  1. 设置 VS2017 工具 > 环境 > 字体和颜色 > 字体:例如“Lucida”
  2. 使用编码“Unicode (UTF-8 with signature) - Codepage 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赞 neonxc 3/24/2021 #13

如果你正在寻找一个可移植的解决方案,不幸的是,它仍然不是C++20标准的一部分,我可以推荐nowide库。它既可以独立使用,也可以作为 boost 的一部分。您会发现许多标准对应物在那里使用或发出 utf-8 编码的 s。是的,s,不是 s(还)。如果您的程序已经运行了 s char8_t,请随意使用 s-remediation 实用程序将 s 解释为 s。charcharchar8_tchar8_tchar

请求的代码片段如下所示:

#include <boost/nowide/iostream.hpp>
#include <char8_t-remediation.h>

int main()
{
    using boost::nowide::cout;

    cout << U8("¡Hola!") << std::endl;
}

注意:请注意流方向问题。在我的回答中,一个简短的建议是:仅对输入/输出和 utf-8 编码数据使用无宽

0赞 yu yang Jian 4/15/2021 #14

就我而言,我正在读取 UTF-8 文件并打印到 ,我发现效果非常好,即使在 Visual Studio 调试器中也能正确显示 UTF-8 单词(我正在阅读繁体中文),来自这篇文章Consolewifstream

#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;
0赞 Changming Sun 1/2/2022 #15

解决方案 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 语言环境还没有出现。然后,您需要知道您将使用哪种语言(中文,法语?),并更改系统设置以匹配它。有系统级设置:Change system code page

以及用户级别设置:enter image description here

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

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

0赞 user3161924 3/8/2022 #16

在 Win10 上使用 VS2019 和 UNICODE 控制台应用进行测试时发现以下测试西班牙语和日语:

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

using:使用西班牙语(墨西哥)Windows 语言设置时,将正确的代码页设置为 CP1252,输出良好(lucida 控制台字体)。但是,日语输出(使用日语 Windows 语言)被禁止显示(这意味着这些字符没有输出,输出普通拉丁字符)。setlocale(LC_ALL, "");

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

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

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

2赞 Michael Haephrati 5/14/2023 #17

我在希伯来语中使用了一个回文词,因为控制台应用程序可能会显示从右到左颠倒的字符串。

这是我的多平台代码:

#include <iostream> 
#ifdef _WIN32 // #A
#include <io.h> // #B
#include <fcntl.h> // #C
#else // #D
#include <locale> // #E
#endif

int main() 
{
#ifdef _WIN32 // #A
    _setmode(_fileno(stdout), _O_U16TEXT); // #F
    std::wcout << L"אבא" << std::endl; // #G
#else // #D
    std::locale::global(std::locale("")); // #H
    std::wcout.imbue(std::locale()); // #I
    std::wcout << L"אבא" << std::endl; // #G
#endif
}

#A - 特定于 Windows 的代码的预处理器指令

#B - 包括用于低级别 I/O 操作的 io.h 库

#C - 包括用于文件控制操作的 fcntl.h 库

#D - 非 Windows 代码的预处理器指令

#E - 包括用于特定于区域设置的操作的区域设置库

#F - 将 stdout 的模式设置为使用 Unicode

#G - 将希伯来语单词打印到控制台

#H - 将全局区域设置设置为用户的首选区域设置

#I - 将 wcout 的区域设置设置为全局区域设置