【问题标题】:C++: output contents of a Unicode file to console in WindowsC ++:将Unicode文件的内容输出到Windows中的控制台
【发布时间】:2011-02-05 19:35:59
【问题描述】:

我已经阅读了很多讨论这个问题的文章和论坛帖子,所有的解决方案对于这样一个简单的任务来说似乎都太复杂了。

这是直接来自 cplusplus.com 的示例代码:

// reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main () {
  string line;
  ifstream myfile ("example.txt");
  if (myfile.is_open())
  {
    while ( myfile.good() )
    {
      getline (myfile,line);
      cout << line << endl;
    }
    myfile.close();
  }

  else cout << "Unable to open file"; 

  return 0;
}

只要 example.txt 只有 ASCII 字符,它就可以正常工作。如果我尝试用俄语添加一些东西,事情就会变得一团糟。

在 GNU/Linux 中,就像将文件保存为 UTF-8 一样简单。

在 Windows 中,这是行不通的。将文件转换为 UCS-2 Little Endian(Windows 似乎默认使用的)并将所有函数更改为对应的 wchar_t 也不起作用。

难道没有某种“正确”的方法可以在不进行各种魔法编码转换的情况下完成这项工作吗?

【问题讨论】:

  • 你可以做到这一点,但它确实需要一些工作。您应该能够通过网络搜索找到所需的信息。此外,Windows 使用 UTF-16 而不是 UCS-2。
  • 放弃在Windows上太复杂了,我试了一次,浪费了很多时间。
  • @Adam Rosenfield:这并不能回答问题。 chcp 65001 不能解决问题。
  • 如何在 windows 和 linux 之间欺骗不同的 UCS2 字节序?

标签: c++ windows unicode console


【解决方案1】:

Windows 控制台在某种程度上支持 unicode。它不支持从左到右和“复杂脚本”。要使用 Visual C++ 打印 UTF-16 文件,请使用以下命令:

   _setmode(_fileno(stdout), _O_U16TEXT);   

并使用wcout 而不是cout

不支持“UTF8”代码页,因此对于 UTF-8,您必须使用 MultiBytetoWideChar

有关 unicode 控制台支持的更多信息,请参阅this blog

【讨论】:

  • 我认为您不能使用 C++ 对象,因为它们总是转换为某种 8 位编码。这意味着您必须使用wprintf,如this blog 中所述。
  • 我尝试了 UTF-8、UCS-2 Big Endian 和 UCS-2 Little Endian 的文件编码。使用 _setmode 和 wcout 时都没有产生清晰的输出。
  • 不支持 UTF-8。您需要使用 UCS-2 和正确的类型/函数(wstring 代替字符串,L"" 用于字符串文字而不是 "")。
  • 我确认 Windows 控制台没有正确显示代理对。例如,Windows 10 中的控制台默认字体 Consolas 支持 emoji ?(可以在 VS 编辑器中使用相同字体查看)。它的代码点是 U+1F600,UTF-16 代理对是 D83D DE00。只需尝试用它 WriteConsoleW(),它将显示为 2 个正方形问号。但是从控制台通过鼠标复制会在剪贴板中提供正确的字符。此外,如果调用 ReadConsoleW() 并将此字符粘贴到控制台,缓冲区将包含相应的代理对。所以,控制台的内部缓冲区是正确的。
【解决方案2】:

在 Windows 上使用 cout 输出到控制台的正确方法是首先调用GetConsoleOutputCP,然后将您拥有的输入转换为控制台代码页。或者,使用WriteConsoleW,传递wchar_t*

【讨论】:

  • 我得到 437,即“IBM437 OEM United States”。 SetConsoleOutputCP(CP_UTF8) 没有帮助。
  • 因此您需要将输入转换为 cp437。请注意,CP_UTF8 的支持不是很好;如果要输出西里尔文,请使用其他一些支持西里尔文的代码页。
【解决方案3】:

要从文件中读取 UTF-8 或 UTF-16 字符串,您可以使用 _wfopen_sfgetws 的扩展 mode 字符串。我认为这些扩展还没有 C++ 接口。 Michael Kaplan's blog 中描述了打印到控制台的最简单方法:

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

int main(void) {
    _setmode(_fileno(stdout), _O_U16TEXT);
    wprintf(L"\x043a\x043e\x0448\x043a\x0430 \x65e5\x672c\x56fd\n");
    return 0;
}

避免使用GetConsoleOutputCP,仅保留它是为了与 8 位 API 兼容。

【讨论】:

  • Michael Kaplan 的博客不存在了(找不到资源
  • 我可以确认 DEC 2018 这仍然没有改变。此外,如果这导致崩溃,如果之后,某些东西使用 printf family 或 std::cout ...
【解决方案4】:

虽然 Windows 控制台窗口基于 UCS-2,但它们不能正确支持 UTF-8。

可能通过使用适当的 API 函数将控制台窗口的活动输出代码页临时设置为 UTF-8 来使事情正常进行。请注意,这些函数区分输入代码页和输出代码页。但是,[cmd.exe] 确实不喜欢 UTF-8 作为活动代码页,所以不要将其设置为永久代码页。

否则,您可以使用 Unicode 控制台窗口函数。

干杯,

【讨论】:

  • @David:喜欢,90%,因为它显然使用非常简单的数组来保存内容。但我还没有尝试过带有控制台窗口的 UTF-16 代理对。如果它有效(有效吗?)我会说,万岁,谢谢,我错了。 :-)
  • @David:是的,当然,它是 UCS-2。 2 bytes per character 没有为代理对留出空间。干杯,谢谢你让我检查这个,
  • 它是 UTF-16。这是一个链接:msdn.microsoft.com/en-us/library/dd374069(v=vs.85).aspx。是时候让 Alf 在他的长篇大论之后发布一些链接了。请注意,您仍然需要支持 >= U+10000 个字符的字体,因此“试一试”并不能证明什么。
  • @Mark:我已经发布了关于控制台窗口的relevant documentation 的链接。您的链接是关于一般 Windows 应用程序的 不相关 文档,您一定已经理解了,因此是谎言。您的“Alf 发布一些链接的时间”是一个谎言,因为您一定已经看到了链接和对它的引用。你的“在他的长篇大论之后”是一个谎言。您的“不能证明任何事情”颠倒了举证责任,这是一个谬论。所以,我在你的回答中算了 3 个谎言和 1 个谬误。另外,实际上是错误的。
  • @Mark @Alf 我相信 Alf 是正确的。您尝试将代理对写入控制台并查看出现了多少字形。但是阿尔夫,没必要这么火爆而烦恼!
【解决方案5】:
#include <stdio.h>

int main (int argc, char *argv[])
{
    // do chcp 65001 in the console before running this
    printf ("γασσο γεο!\n");
}

完美运行如果您在运行程序之前在控制台中执行chcp 65001

注意事项:

  • 我正在使用 64 位 Windows 7 和 VC++ Express 2010
  • 代码在一个编码为 UTF-8 的文件中,没有 BOM - 我是在文本编辑器中编写的,没有使用 VC++ IDE,然后使用 VC++ 编译它。
  • 控制台有 TrueType 字体 - 这很重要

不知道这些东西是不是影响太大了……

不能为 BMP 上的字符说话,试一试并发表评论。

【讨论】:

  • chcp 65001 不起作用,问微软为什么决定不支持它。
  • Tnx 你解决了我的问题。我是法国人,对我来说好的代码页是 819。(所以 +1)
  • 我终于解决了我的程序在开始使用 SetConsoleOutputCP(1252) 时更改代码页的问题。
【解决方案6】:

为了清楚起见,这里有些人提到了 UTF8。 UTF8 是一种多字节格式,在某些文档中被错误地称为 Unicode。 Unicode 总是只有两个字节。

我已经在 Visual Studio 2008 中使用了这个先前发布的解决方案。我不知道是否适用于更高版本的 Visual Studio。

   #include <iostream>
   #include <fnctl.h>
   #include <io.h>
   #include <tchar.h>

   <code ommitted>


   _setmode(_fileno(stdout), _O_U16TEXT); 

   std::wcout << _T("This is some text to print\n");

我使用宏在 std::wcout 和 std::cout 之间切换,还删除了 ASCII 构建的 _setmode 调用,从而允许为 ASCII 和 UNICODE 编译。这行得通。我尚未使用 std::endl 进行测试,但我可能会使用 wcout 和 Unicode(不确定),即

   std::wcout << _T("This is some text to print") << std::endl;

【讨论】:

猜你喜欢
  • 2011-02-20
  • 1970-01-01
  • 2015-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多