在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个回答

109

我已经验证了这个解决方案,使用的是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


4
+1并删除了我的答案。这是我们在Instalog中选择的方法。 - Billy ONeal
8
但是它仍然无法在我的控制台中显示日语字符。 - sarat
2
+1 对于可行的解决方案表示赞赏,但是需要注意的是这是一个 Visual C++ 特定的解决方案:它不一定适用于 g++。 - Cheers and hth. - Alf
13
当你同时有std::cout输出时,会导致程序无法正常工作。根据cplusplus.com的说法,程序不应该在wcout上进行输出操作与在cout(或在标准输出上进行其他窄向输出操作)混合使用:一旦在任何一个输出流上执行了输出操作,标准输出流就会获得一个方向(窄向或宽向),只能通过调用freopen函数安全地改变它。 - Roger Dahl
2
在处理答案中的字符串时,对于像L"안녕하세요."L"你好!"这样更长的字符无法正常工作。看起来,默认控制台字体不支持这些字符(这可能取决于Windows中的区域设置)。 - Roi Danton
显示剩余7条评论

19
您可以使用开源的 {fmt} 库来便携地打印Unicode文本,包括在Windows上,例如:
#include <fmt/core.h>

int main() {
  fmt::print("èéøÞǽлљΣæča");
}

输出:

èéøÞǽлљΣæča

这需要在MSVC中使用编译器选项/utf-8进行编译。
我不建议使用wcout,因为它不具备可移植性,甚至在Windows上使用也需要额外的努力,例如:
std::wcout << L"èéøÞǽлљΣæča";

将打印:

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

在俄语Windows中(ACP 1251,控制台CP 866)。
免责声明:我是{fmt}的作者。

11

使用Unicode在中文环境下打印Hello World

下面是一个在中文环境下打印 "Hello" 的示例。我在 Windows 10 上测试通过,但我认为自 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[])

此外,我认为我们应该使用wmain函数代替main函数。它们都可以工作,但在Unicode环境中,wmain可能更加方便。

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

2 控制台代码页

这很明显。我们需要控制台中的Unicode代码页。如果您想检查默认代码页,请打开控制台并键入chcp而不带任何参数。我们必须将其更改为65001,这是UTF-8代码页。Windows代码页标识符有一个预处理器宏,表示该代码页:CP_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");
返回:返回值很有趣。它将包含一个字符串,描述确切选择了哪个语言环境。只需尝试一下:-)我用chinesegerman进行了测试。更多信息:setlocale 使用宽字符输出: 不用说太多。 如果您想要输出宽字符,请使用以下示例:
std::wcout << L"你好" << std::endl;

哦,还有不要忘记对宽字符使用 L 前缀!如果您在源文件中键入了文字Unicode字符,则源文件必须是Unicode编码的。像Visual Studio默认的UTF-16-LE一样。或者可以使用notepad++并将编码设置为UCS-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字符。您仍需要在设置中配置支持您字符的字体。它是由微软开发的开源项目,可以在GitHub上获取,也可以从Microsoft Store安装。我在Windows 10上成功尝试了这个方法。


这对我不起作用。使用C语言和"wprintf(L"你好");"。 - zezba9000
将std :: copy更改为“memcpy(fontInfo.FaceName,myFont,(sizeof(myFont)));”,在C ++中使用.cpp文件可以正常工作,但如果我编译为.c文件,则无法正常工作。 - zezba9000
1
没事了,它正在工作。只需要确保你的源文件是正确的UTF-8编码(带有签名)。 - zezba9000
SetCurrentConsoleFontEx 函数处于支持模式,不再鼓励在新的命令行应用程序中使用。 - Luan Vitor

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
谢谢提供新的想法,它对于这个字符串起作用了,但是对于像“¡Hola! αβγ ambulō привет :)”这样更复杂的内容则失败了。 - Andrew
这个字符串对我在 wprintf 上也没用,结果就是完全空白。wcout 至少有些字符是正确的。你能双重检查一下 wprintf 是否可以正确处理这个字符串吗? - Puppy
是的,如果您为控制台选择正确的字体并使用cmd.exe启动它,它就可以工作。 - Andrew
4
不支持地区设置想法,应该避免使用"_tmain"和"_TCHAR"。修复地区设置只支持该地区的Windows ANSI编码字符,不支持一般的Unicode输出(甚至不支持UCS2)。 - Cheers and hth. - Alf
似乎可以工作。不幸的是,现在我的数字有分组(千位)分隔符。 :( - Adrian

2

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

这是我的跨平台代码:

#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 - 包含io.h库以进行低级I/O操作

#C - 包含fcntl.h库以进行文件控制操作

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

#E - 包含locale库以进行区域设置特定操作

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

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

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

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


1

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);

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

更改显示字体为Lucidia Console,否则UTF-8输出将完全无法工作。

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

测试环境:德语Windows XP SP3


1
你可以使用字符常量,例如\x45,使字符串在不考虑源编码的情况下正常工作。 - phuclv
2
-1 不好的建议是欺骗编译器,导致宽字面量编译不正确。 - Cheers and hth. - Alf
你真的想要使用转义字符,这样就不会依赖于编辑器中非ASCII字符的保存方式和编译器的解释方式。例如,答案中的UTF-8字符串可以以可移植的方式写成"tr\xc3\xa4nen\xc3\xbcberstr\xc3\xb6mt\xe2\x84\xa2" - user4815162342
2
SetConsoleCP 只影响输入,所以它在你的示例中不起作用并不奇怪。控制输出编码的是 SetConsoleOutputCP - rdb

0

mswcrt和io流存在一些问题。

  1. _setmode(_fileno(stdout), _O_U16TEXT)技巧只适用于MS VC++而不是MinGW-GCC。此外,它有时会因Windows配置而导致崩溃。
  2. SetConsoleCP(65001)用于UTF-8。在许多多字节字符场景中可能会失败,但对于UTF-16LE始终可以使用。
  3. 您需要在应用程序退出时恢复以前的控制台代码页。

Windows控制台支持使用UTF-16LE模式的ReadConsole和WriteConsole函数进行UNICODE。背景效果-在这种情况下进行管道操作将无法工作。即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

屏幕截图:


0

如果你正在寻找一种便携式解决方案,很遗憾,截至 C++20,它仍未成为标准的一部分,我可以推荐 nowide 库。它可以作为 独立库 或作为 Boost 的一部分提供。你会在那里找到许多消耗或发出 utf-8 编码的 char 的标准对应项。是的,是 char 而不是 char8_t(目前还没有)。如果你的程序已经使用了 char8_t,可以随意使用char8_t-矫正实用工具 将其解释为 char

请求的代码片段如下:

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

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

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

请注意流方向问题。在我的回答上下文中的一个短暂建议是:仅使用nowide流进行输入/输出和UTF-8编码数据。

0

默认编码开启:

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

我的解决方案步骤,包括空字符\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

2. 创建转换函数,将std :: wstring转换为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;
}
  • 打印 std::string。 参考 RawString Suffix
  • 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上进行测试;std=c++17。(Windows项目)
    • 在带有g++的Microsoft Visual Studio 2017上进行测试;std=c++17。(Linux项目)
    • 在带有g++的Jetbrains Clion 2018.3上进行测试;std=c++17。(Linux工具链/远程)

    QA

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

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

    Q. std::wstring 是否跨平台?
    A. 不是。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 是否跨平台?
    A. 是的。std::string 使用 char 元素。char 类型保证在所有编译器中具有相同的字节大小。char 类型大小为1个字节。请查看UNIX、Linux 和 Windows 上的标准数据类型


    0

    首先,抱歉我可能没有所需的字体,因此我还无法测试它。

    这里看起来有点可疑。

    // 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;
    

    那是有趣的部分。我尝试了一下,惊讶地发现它不起作用,但还是谢谢。 - Andrew

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