12

我目前正在编写一个应用程序,它需要我在任意窗口上调用 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,即使新的语言环境在全球范围内并明确地应用于所有标准流,我也遇到了同样的问题。

4

4 回答 4

20

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

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

您必须添加这 2 个包含文件

#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";
}
于 2012-03-26T09:02:40.253 回答
2

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

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

于 2012-03-25T11:18:53.420 回答
2

我刚刚测试了 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++ 标准库实现中,输出都被严重破坏了。

于 2012-03-25T13:31:55.577 回答
1

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

于 2012-03-25T17:09:58.183 回答