【问题标题】:How to output and input UTF8 or UTF16 Unicode text in Windows using C++?如何使用 C++ 在 Windows 中输出和输入 UTF8 或 UTF16 Unicode 文本?
【发布时间】:2019-09-16 11:28:09
【问题描述】:

这是我的程序:

#include <iostream>
#include <string>
#include <locale>
#include <clocale>
#include <codecvt>
#include <io.h>
#include <fcntl.h>

int main()
{
    fflush(stdout);
    _setmode(_fileno(stdout), _O_U16TEXT);
    std::ios_base::sync_with_stdio(false);
    std::setlocale(LC_ALL, "el_GR.utf8");
    std::locale loc{ "el_GR.utf8" };
    std::locale::global(loc);       // apparently this does not set the global locale
    //std::wcout.imbue(loc);
    //std::wcin.imbue(loc);

    std::wstring yes;
    std::wcout << L"It's all good γεια ναί" << L'\n';
    std::wcin >> yes;
    std::wcout << yes << L'\n';
    return 0;
}

假设我想支持希腊编码(对于 both 输入和输出)。如果我设置了适当的编码并且当然删除了fflush(stdout)_setmode(),这个程序可以在Linux 上完美运行各种输出和输入语言。

所以在 Windows 上,当我使用 std::locale::global(loc) 时,该程序将正确输出希腊语(和英语),但它不会接受我从键盘输入的希腊语输入。如果我输入希腊语,std::wcout &lt;&lt; yes 会输出乱码或问号。显然::global 在 Windows 上不是真正的全局变量?

所以我在wcoutwcin(也适用于Linux)上尝试了.imbue() 方法,您在此处看到注释掉了。当我使用这两个语句中的任何一个并运行程序时,它会(正确编译)向我显示一个提示,当我按 w/e 然后按“输入”时,它会简单地退出而没有错误或诸如此类。

我尝试了一些特定于 Windows 的命令,但后来我也感到困惑。我不清楚我应该在 Windows 上尝试什么以及何时尝试。

所以问题是我如何才能像上面的程序一样在 Windows 中正确输入和输出希腊文本?我使用 MSVS 2017 最新更新。提前致谢。

【问题讨论】:

  • 编辑“给我一个提示,当我按下时”:它不会给我一个提示。它不会给我任何东西。控制台只允许我输入,当我按 enter 时它只是退出。请注意,我还在 MSVS 上设置了“使用 Unicode 字符集”,并且还有 Lucida 控制台。
  • Streams 默认为 ANSI 模式,对于控制台,这使用其旧代码页 API,因此读取希腊输入取决于控制台输入代码页是 737 还是 1253,可以通过 SetConsoleCP 设置。我认为 CRT 的最新版本允许通过其宽字符 UTF-16 API 从控制台读取。试试_setmode(_fileno(stdin), _O_U16TEXT)
  • @ErykSun 是的!有用!我不需要SetConsoleCP,只是_setmode(_fileno(stdin), _O_U16TEXT) 做到了。但是,当我在std::wcoutstd::wcin.imbue 时,它会输出错误:“Debug Assertion Failed! Program: ... File: minkernel\crts\ucrt\src\appcrt\stdio\fgetc.cpp line: 50表达式: ((_Stream.is_string_backed())) || (fn = _fileno(_Stream.public_stream()), ((_textmode_safe(fn) == __crt_lowio_text_mode::ansi) && !_tm_unicode_safe(fn)))) ..`
  • @Nikos,备份几个 cmets 到我讨论通过 GetConsoleMode 检测控制台的地方(成功)。如果我们有控制台,那么我们必须使用_O_U16TEXT,因为通常不支持将 UTF-8 与控制台的旧代码页 API 一起使用。 (Windows 8+ 通过代码页 65001 支持 UTF-8 输出,但所有版本的 UTF-8 输入仍然中断。)
  • 您可以以 UTF16 格式读取/写入用户数据,并以 UTF8 格式读取/写入文件以实现兼容性。使用WideCharToMultiByte(CP_UTF8,...)/MultiByteToWideChar 进行UTF16/UTF8 转换。

标签: c++ windows winapi locale facet


【解决方案1】:

正如@Eryk Sun 在 cmets 中提到的,我不得不使用 _setmode(_fileno(stdin), _O_U16TEXT);

Windows UTF-8 控制台输入仍然(截至 2019 年)有些损坏。

编辑:

以上修改还不够。现在,每当我想在 Windows 上支持 UTF-8 代码页和 UNICODE 输入/输出时,我都会执行以下操作(请阅读代码 cmets 了解更多信息)。

int main()
{
    fflush( stdout );
#if defined _MSC_VER
#   pragma region WIN_UNICODE_SUPPORT_MAIN
#endif
#if defined _WIN32
    // change code page to UTF-8 UNICODE
    if ( !IsValidCodePage( CP_UTF8 ) )
    {
        return GetLastError();
    }
    if ( !SetConsoleCP( CP_UTF8 ) )
    {
        return GetLastError();
    }
    if ( !SetConsoleOutputCP( CP_UTF8 ) )
    {
        return GetLastError();
    }
    
    // change console font - post Windows Vista only
    HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
    CONSOLE_FONT_INFOEX cfie;
    const auto sz = sizeof( CONSOLE_FONT_INFOEX );
    ZeroMemory( &cfie, sz );
    cfie.cbSize = sz;
    cfie.dwFontSize.Y = 14;
    wcscpy_s( cfie.FaceName,
        L"Lucida Console" );
    SetCurrentConsoleFontEx( hStdOut,
        false,
        &cfie );
        
    // change file stream translation mode
    _setmode( _fileno( stdout ), _O_U16TEXT );
    _setmode( _fileno( stderr ), _O_U16TEXT );
    _setmode( _fileno( stdin ), _O_U16TEXT );
#endif
#if defined _MSC_VER
#   pragma endregion
#endif
    std::ios_base::sync_with_stdio( false );
    // program:...

    return 0;
}

指南:

  • 在项目属性 -> 常规 -> 字符集中使用“使用 Windows 字符集”
  • 确保使用支持 unicode utf-8 的终端字体(打开控制台 -> 属性 -> 字体 -> “Lucida 控制台”在 Windows 上是理想的)。上面的代码会自动设置。
  • 使用string 和8 位chars。
  • 使用 16 位 chars(wchar_twstring 等)与 Windows 控制台交互
  • 在应用程序边界使用 8 位 chars/string(例如写入文件、与其他操作系统交互等)
  • string|char 转换为 wstring|wchar_t 以与 Windows API 交互

【讨论】:

  • 未来是否有解决此问题的计划? 20 年 11 月 13 日仍然坏掉...
  • @varvir Windows 10 正在走向新的控制台“ecosystem”。阅读更多关于它的信息。我已经更新了我的答案。
  • 我用下面两行解决了 utf8 输入问题。 _setmode( _fileno( stdout ), _O_U8TEXT ); _setmode( _fileno( stderr ), _O_U8TEXT ); 为什么这还不够?抱歉,我不知道 Win32 API 和 C 运行时。而SetConsoleCP SetConsoleOutputCP 不起作用? ref1ref2ref3
  • @varvir _setmode 是不够的,因为它只是设置了文件蒸汽字符翻译模式。首先将代码页设置为 Unicode UTF-8(因为默认情况下它是一个名为“OEM”的 ANSI 代码页变体)。其次,为支持附加字形的 Windows 控制台应用适当的字体。 “Lucida 控制台”完成了这项工作。最后确保您的项目属性 -> General -> Character Set 设置为 Use Unicode。使用wcoutwcinwstring 和家人。不过,您可以使用 8 位字符来写入/读取文件系统文件。阅读问题的 cmets 和代码 cmets。
猜你喜欢
  • 2011-03-29
  • 2014-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 2011-02-20
  • 2016-07-29
  • 2012-04-09
相关资源
最近更新 更多