繁体   English   中英

Output Unicode 控制台中的字符串 Windows

[英]Output Unicode strings in Windows console

您好,我试图将 output 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 正确访问 output 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;

编辑除了将此代码段包装在 stream 中之外,我找不到任何其他解决方案。希望有人有更好的主意。

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

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

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[])

另外我认为我们应该使用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

5.使用宽字符输出

这里不多说。 如果要输出宽字符,请使用以下示例:

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

哦,不要忘记宽字符的L前缀! 如果您在源文件中键入这样的文字 unicode 字符,则源文件必须是 unicode 编码的。 就像 Visual Studio 中的默认值是 UTF-16-LE。 或者也许使用记事本++并将编码设置为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;
}

干杯!

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 (Byte Order Mark; Signature).源代码必须保存为BOM(字节顺序标记;签名)的 UTF-8。 然后,Microsoft 编译器cl.exe按原样采用 UTF-8 字符串。
BOM, cl.exe transcodes the string to ANSI (ie CP1252), which doesn't match to CP65001 (= UTF-8).如果此代码BOM 一起保存,cl.exe 会将字符串转码为 ANSI(即 CP1252),这与 CP65001 (= UTF-8) 不匹配。

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

  • 类型: chcp
  • 答案: 850
  • 类型: test.exe
  • 答案: tr├ñnen├╝berstr├ÂmtÔäó
  • 类型: chcp
  • 答案: 65001
  • 类型: chcp 65001
  • 类型: test.exe
  • 答案: tränenüberströmt™

测试:德国 Windows XP SP3

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

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

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

这是我的多平台代码:

#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 的语言环境设置为全局语言环境

您可以使用开源 {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} 的作者。

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

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

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

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

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

注意:请注意流方向问题 在我的回答上下文中,一个简短的建议是:对输入/输出和 utf-8 编码数据专门使用nowide

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

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

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

在 Windows 控制台上浏览一篇关于java unicode的有趣文章

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

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

这里的东西看起来有点可疑

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

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 将 ret.log 文件变为 0 字节。 如果你对这个事实没意见,你可以尝试我的图书馆,如下所示。

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

截屏:

默认编码:

  • 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
  1. 创建转换函数 std:: w string 到 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

}

窗口代码。 使用 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 项目)
  • 在 Microsoft Visual Studio 2017 上使用 g++ 进行测试; 标准=c++17。 (Linux 项目)
  • 在 Jetbrains Clion 2018.3 上使用 g++ 进行测试; 标准=c++17。 (Linux 工具链/远程)

质量保证

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

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

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

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

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

  1. 设置VS2017工具>环境>字体和颜色>字体:例如'Lucida'
  2. 使用编码“Unicode(带签名的 UTF-8)-代码页 650001”保存 C++ 源文件,以便您可以在没有编译器警告的情况下输入重音字符 L"âéïôù",同时避免在任何地方使用双字节字符
  3. 使用 Configuration Properties > General > CharacterSet > "Use Multi-byte.." 和 Configuration Properties > C/C++ > All Options > Additional Options > "/utf-8" 标志编译
  4. #include <iostream>、<io.h> 和 <fcntl.h>
  5. 执行一个晦涩的 '_setmode(_fileno(stdout), _O_WTEXT);' 一次在应用程序开始时
  6. 忘记 'cout <<... ;' 并且只使用 'wcout << ... ;'

备注,Win7 上的 VS2015 需要一个“SetConsoleOutputCP(65001);” 并允许通过 wcout 和 cout 混合输出。

在我的情况下,我正在阅读 UTF-8 文件并打印到Console ,我发现wifstream工作得非常好,即使在 Visual Studio 调试器中也能正确显示 UTF-8 单词(我正在阅读繁体中文),来自这篇文章

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

方案一:使用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 函数了。

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

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

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

使用:'_setmode(_fileno(stdout), _O_U16TEXT);` output 适用于所有人。 但是,所有 output 都是 16 位的,因此重定向到文件会输出 16 位字符。

使用: printfUTF-8和 8834335060888 文本 output 和SetConsoleOutputCP(CP_UTF8)也有效(但如果您在setlocale(LC_ALL, ""); -我必须删除它才能使 output 正常工作)。

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

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

长话短说:

  1. 使用chcp查找适合您的代码页。 就我而言,它是西欧的chcp 28591
  2. 可选择将其REG ADD HKCU\\Console /v CodePage /t REG_DWORD /d 28591默认值: 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与价值28591的HKCU \\控制台注册表项中的数据。 这确实对我有用。

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

我有一个类似的问题, 使用 C++ 将 Unicode 输出到控制台,在 Windows 中包含在运行程序之前需要在控制台中执行chcp 65001的 gem。

可能有一些以编程方式执行此操作的方法,但我不知道它是什么。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM