在Windows控制台应用程序中输出unicode字符串


72

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

我发现了这一点: 在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 2010年

1
它显示占位符而不是字符。我看起来并没有很深入。我只能说的是,由于某种原因,发送给wcout或cout的同一字符串变得疯狂,而wprintf毫无问题地显示了它。
安德鲁(Andrew)2010年

只有一些Unicode字符可以在Win32控制台中正确显示。控制台不支持过于复杂的字符或具有影响其大小的组合标记的字符。尝试一下WriteConsoleW-如果不行,那就不可能了。
user541686 2012年

Answers:


92

我已经在这里使用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

6
仍然没有在我的控制台中显示日语字符。
萨拉特

1
+1表示可行的修复程序,应注意这是一种特定于Visual C ++的解决方案:它不一定适用于g ++。
干杯和健康。-Alf 2014年

10
Doesn't work when you also have std::cout's来自cplusplus.com:程序不应将wcout上的输出操作与cout上的输出操作(或stdout上的其他窄方向输出操作)混合:一旦对其中一个执行输出操作,则标准输出流将获得方向( (窄或宽)),只有在stdout上调用freopen才能安全地进行更改。
罗杰·达尔2014年

1
@RogerDahl:我并没有尽力,但是(在警告中提到了MS文档)似乎可以在调用fflush()之后重新设置它。因此,在明确的_setmode()之后wcout << ...,我做了`wcout << flush; fflush(stdout); _setmode(_fileno(stdout),_O_TEXT);`似乎有效。
pepr

6

中文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。我创建了一个空白的控制台应用程序。可以使用默认设置。但是,如果您遇到问题或使用其他想法,则可能需要检查以下问题:

在项目属性中,找到配置属性->常规->项目默认值->字符集。它应该是“使用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");

返回值很有趣。它将包含一个字符串,以准确描述选择的语言环境。尝试一下:-)我用chinese和测试过german。更多信息:setlocale

5.使用宽字符输出

这里没有太多要说的。如果要输出宽字符,请使用此示例:

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

干杯!


这对我不起作用。将C与“ wprintf(L”你好“);”一起使用
zezba9000 '19

将std :: copy更改为“ memcpy(fontInfo.FaceName,myFont,(sizeof(myFont)));”;它在C ++中可以与.cpp文件一起正常工作,但如果我使用.c文件为C进行编译则不能。
zezba9000 '19

1
NVR头脑,它的工作原理。只需确保您的源文件是正确的UTF-8编码(带有签名)即可。
zezba9000

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ōпривет:)!”
安德鲁·

该字符串对我来说在wprintf上也不起作用,只是完全空白了。wcout至少有一些正确的字符。您能否再次检查一下wprintf是否正确获取了此字符串?
Puppy'4

是的,如果您为控制台选择正确的字体并使用cmd.exe启动它,则它可以正常工作
Andrew

4
-1用于语言环境提示+_tmain和的使用_TCHAR。修复语言环境仅支持该语言环境的Windows ANSI编码中的字符。它不支持常规Unicode输出(甚至不支持UCS2)。
干杯和健康。-Alf 2014年

似乎可以工作。不幸的是,现在我的数字有分组(千)分隔符。:(
阿德里安

2

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 2014年

1
-1不利于欺骗编译器的建议,导致对宽文字进行错误的编译。
干杯和健康。-Alf

您真的想使用转义符,以便不依赖于源代码中的非ASCII字符由编辑器保存和由编译器解释的方式。例如,答案中的UTF-8字符串可移植为"tr\xc3\xa4nen\xc3\xbcberstr\xc3\xb6mt\xe2\x84\xa2"
user4815162342

1
SetConsoleCP只影响输入,因此在您的示例中它不起作用也就不足为奇了。就是SetConsoleOutputCP控制输出编码。
rdb


0

现在我想将unicode从Python流传输到Windows控制台,这是我需要做的最少工作:

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

在Windows控制台上浏览有关Java Unicode的有趣文章

此外,在这种情况下,在Python中您无法写入默认的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 2011年

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

0

mswcrt和io流存在一些问题。

  1. 技巧_setmode(_fileno(stdout),_O_U16TEXT); 仅适用于MS VC ++,不适用于MinGW-GCC。此外,有时会导致崩溃,具体取决于Windows配置。
  2. 适用于UTF-8的SetConsoleCP(65001)。在许多多字节字符场景中可能会失败,但是对于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

默认编码为:

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

我的解决方案步骤包括null字符\ 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上测试; std = c ++ 17。(Windows项目)
  • 使用g ++在Microsoft Visual Studio 2017上测试; std = c ++ 17。(Linux项目)
  • 使用g ++在Jetbrains Clion 2018.3上测试; std = c ++ 17。(Linux工具链/远程)

质量检查

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

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

问: 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上检查标准数据类型

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


0

您可以使用开源{fmt}库来可移植地打印Unicode文本,包括在Windows上,例如:

#include <fmt/core.h>

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

输出:

èéøÞǽлљΣæča

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

我不建议使用,wcout因为它是不可移植的,而且如果不付出额外的努力就无法在Windows上运行,例如:

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

将打印:

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

在俄语Windows中(ACP 1251,控制台CP 866)。

免责声明:我是{fmt}的作者。



-1

在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 cosole的系统菜单中选择的字体:

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

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

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

chcp 28591

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

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

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

这将CodePage使用28591HKCU \ Console注册表项中数据。那确实对我有用。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.