【问题标题】:Windows Unicode C++ Stream Output FailureWindows Unicode C++ 流输出失败
【发布时间】:2012-04-09 04:23:30
【问题描述】:

我目前正在编写一个应用程序,该应用程序需要我在任意窗口上调用 GetWindowText 并将该数据存储到文件中以供以后处理。长话短说,我注意到我的工具在战地 3 上失败了,我将问题缩小到窗口标题中的以下字符: http://www.fileformat.info/info/unicode/char/2122/index.htm

所以我创建了一个小测试应用程序,它只执行以下操作:

std::wcout << L"\u2122";

在程序的其余部分中,这会中断到控制台窗口的输出。

当 MessageBoxW 等 API 正常显示时,为什么 MSVC STL 会在这个字符(我假设是其他字符)上窒息?

如何将这些字符打印到我的文件中?

在 Windows 7 x64 下在 VC10 和 VC11 上测试。

对不起,这篇文章的结构很糟糕,我把头发扯掉了。

谢谢。

编辑:

最小的测试用例

#include <fstream>
#include <iostream>

int main()
{
  {
    std::wofstream test_file("test.txt");
    test_file << L"\u2122";
  }

  std::wcout << L"\u2122";
}

预期结果:“™”字符打印到控制台和文件。 观察结果:文件已创建但为空。没有输出到控制台。

我已经确认我用于控制台的字体能够显示有问题的字符,并且文件肯定是空的(大小为 0 字节)。

编辑:

进一步调试表明,在流中设置了“failbit”和“badbit”。

编辑:

我也尝试过使用 Boost.Locale,即使新的语言环境在全球范围内并明确地应用于所有标准流,我也遇到了同样的问题。

【问题讨论】:

    标签: c++ windows unicode stl wofstream


    【解决方案1】:

    要写入文件,必须正确设置语言环境,例如如果要将它们写入为 UTF-8 字符,则必须添加

    const std::locale utf8_locale
                = std::locale(std::locale(), new std::codecvt_utf8<wchar_t>());
    test_file.imbue(utf8_locale);
    

    你必须添加这两个包含文件

    #include <codecvt>
    #include <locale>
    

    要写入控制台,您必须通过添加将控制台设置为正确的模式(这是特定于 Windows 的)

    _setmode(_fileno(stdout), _O_U8TEXT);
    

    (如果您想使用 UTF-8)。

    为此,您必须添加以下 2 个包含文件:

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

    此外,您必须确保您使用的是支持 Unicode 的字体(例如 Lucida Console)。您可以在控制台窗口的属性中更改字体。

    现在完整的程序如下所示:

    #include <fstream>
    #include <iostream>
    #include <codecvt>
    #include <locale>
    #include <fcntl.h>
    #include <io.h>
    
    int main()
    {
    
      const std::locale utf8_locale = std::locale(std::locale(),
                                        new std::codecvt_utf8<wchar_t>());
      {
        std::wofstream test_file("c:\\temp\\test.txt");
        test_file.imbue(utf8_locale);
        test_file << L"\u2122";
      }
    
      _setmode(_fileno(stdout), _O_U8TEXT);
      std::wcout << L"\u2122";
    }
    

    【讨论】:

    • 好吧,我会被诅咒的,灌输 UTF8 语言环境确实有效......现在为什么 Boost.Locale 不为我做这件事?我将文档解释为假定 UTF-8 是默认的窄编码,并且我已经将语言环境融入了全局和所有静态流,那么到底是什么......
    【解决方案2】:

    虽然宽字符流将 Unicode 作为输入,但这并不是它们作为输出产生的 - 字符经过转换。如果一个字符不能以它要转换的编码表示,则输出失败。

    【讨论】:

    • 这似乎很“错误”(因为没有更好的词)。我不确定我是否了解如何实际解决/修复您所说的内容......
    • 我也不认为这是真的。 std::wstringstream 绝对是宽字符流(继承自 std::wstream),但不做任何转换。
    【解决方案3】:

    我刚刚测试了 GCC(版本 4.4 到 4.7)和 MSVC 10,它们都出现了这个问题。

    同样糟糕的是wprintf,它的作用与 C++ 流 API 一样少。

    我还测试了原始 Win32 API 以查看是否没有其他原因导致失败,这很有效:

    #include <windows.h>
    int main()
    { 
        HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
        DWORD n;
        WriteConsoleW( stdout, L"\u03B2", 1, &n, NULL );
    }
    

    β 写入控制台(如果您将 cmd 的字体设置为 Lucida Console 之类的字体)。

    结论:wchar_t 的输出在两个大型 C++ 标准库实现中都严重损坏。

    【讨论】:

    • 它没有严重损坏,只是记录得很糟糕。
    • 你会说我的选择是什么?使用原始 API 的重写将涉及数千行代码。 Boost.Locale 似乎也没有解决问题......
    • 我手边没有 Nicolai Josuttis 的The C++ Standard Library,但它是关于这个主题的明确书籍。并且考虑到 IOStreams 位是由 Dietmar Kühl 共同编写的 ;) ,它确实很好地涵盖了 IOStream 中的整个字符转换内容。
    【解决方案4】:

    您是一直使用std::wcout 还是有时使用std::cout?混合这些是行不通的。当然,错误描述“窒息”并没有说明您正在观察什么问题。但是,我怀疑这与使用文件的问题不同。

    由于没有对问题的真实描述,因此需要一些水晶球,然后在黑暗中射击才能解决问题...由于您想从文件中获取 Unicode 字符,请确保文件流向您正在使用 std::localestd::codecvt&lt;...&gt; 方面实际上转换为合适的 Unicode 编码。

    【讨论】:

    • 我一直在使用宽类型和 API。甚至像我在问题中发布的那句话这样简单的东西在我的平台上也失败了。如果您将 wcout 替换为 wofstream,则同上。
    • 我添加了一个最小的测试用例。
    • 您是否验证了默认 std::locale 使用的 std::codecvt&lt;wchar_t, char, mbstate_t&gt; 使用 Unicode 感知编码? Boost 似乎有一个UTF-8 facet。我怀疑您平台上的std::wcout 使用std::basic_filebuf&lt;wchar_t&gt;,即它适用于文件和控制台输出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-23
    • 2018-04-25
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多