在Windows中,如何将Unicode文件的内容输出到控制台?(C++)

6
我是一名有用的助手,可以为您翻译文本。
我已经阅读了许多关于这个问题的文章和论坛帖子,所有的解决方案似乎都对于这样一个简单的任务来说过于复杂。
以下是直接从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。 - David Heffernan
请显示扩展ASCII字符。 - Adam Rosenfield
放弃吧,在Windows上太复杂了,我曾经尝试过一次,浪费了很多时间。 - toto
@Adam Rosenfield:那并没有回答问题。chcp 65001并不能解决问题。 - Nikolai
如何在Windows和Linux之间欺骗不同的UCS2字节序? - Sandburg
6个回答

6

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

   _setmode(_fileno(stdout), _O_U16TEXT);   

请使用wcout代替cout

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

有关控制台对Unicode的支持的更多信息,请参见此博客


我认为你不能使用C++对象,因为它们总是转换为某些8位编码。这意味着你必须使用wprintf,就像这篇博客中所描述的那样。 - Philipp
我尝试了UTF-8、UCS-2大端和UCS-2小端作为文件编码。但是在使用_setmode和wcout时,都没有产生可读的输出。 - Nikolai
不支持UTF-8编码。您需要使用UCS-2编码以及正确的类型/函数(使用wstring代替string,使用L""代替""来表示字符串字面值)。 - John
我确认Windows控制台无法正确显示代理对。例如,Windows 10中的控制台具有默认字体Consolas,支持表情符号(可以在使用相同字体的VS编辑器中进行检查)。它的代码点是U+1F600,UTF-16代理对为D83D DE00。只需尝试使用WriteConsoleW()写入该字符,它将显示为2个带问号的方块。但是,通过鼠标从控制台复制会在剪贴板中得到正确的字符。此外,如果调用ReadConsoleW()并将此字符粘贴到控制台中,则缓冲区将包含相应的代理对。因此,控制台的内部缓冲区是正确的。 - dmitry1100

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

我得到了437,它是“IBM437 OEM美国”。SetConsoleOutputCP(CP_UTF8)没有帮助。 - Nikolai
所以你需要将输入转换为cp437。请注意,CP_UTF8支持不太好;如果您想输出西里尔文,请使用一些支持西里尔文的其他代码页。 - Martin v. Löwis

1

如果要从文件中读取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的博客已经不存在了(资源未找到)。 - Salvador
我可以确认到2018年12月,这个问题仍然没有改变。如果之后有任何东西使用printf家族或者std::cout,会导致崩溃。 - user10133158

0
#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 不起作用,请问微软为什么决定不支持它。 - sorin
谢谢你解决了我的问题。我是法国人,对我来说适合的代码页是819。(所以+1) - Lynch
我终于通过在程序开始时使用SetConsoleOutputCP(1252)更改代码页来解决了我的程序问题。 - Lynch

0

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

您可以使用适当的API函数将控制台窗口的活动输出代码页临时设置为UTF-8,从而使事情正常工作。请注意,这些函数区分输入代码页和输出代码页。但是,[cmd.exe]真的不喜欢UTF-8作为活动代码页,因此不要将其设置为永久代码页。

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

祝好运!


@David:大概有90%的可能性,因为它似乎使用非常简单的数组来保存内容。但我还没有在控制台窗口中尝试UTF-16代理对。如果它可以工作(它可以吗?),我会高兴地说,谢谢你,我错了。 :-) - Cheers and hth. - Alf
@David:是的,没错,它是UCS-2。每个字符2个字节(http://msdn.microsoft.com/en-us/library/ms682013%28v=VS.85%29.aspx)不留下代理对的空间。谢谢你让我检查这个问题。 - Cheers and hth. - Alf
1
这是UTF-16编码。这里有一个链接:http://msdn.microsoft.com/en-us/library/dd374069(v=vs.85).aspx。在Alf的长篇抨击之后,是时候发布一些链接了。请注意,您仍需要支持>= U+10000字符的字体,因此“只是尝试”并不能证明任何事情。 - Mark Tolonen
1
@Mark:我已经发布了有关控制台窗口的相关文档链接,你的链接是与Windows应用程序无关的文档,你肯定明白这一点,所以这是一个谎言。你说“现在是Alf发布一些链接的时候了”也是个谎言,因为你必须已经看到了链接和对它的引用。你说“在他的发泄之后”也是个谎言。你说“这并不能证明什么”是在颠倒证明责任,这是一个谬误。所以,在你的回复中,我数了3个谎言和1个谬误。此外,它事实上是错误的。 - Cheers and hth. - Alf
1
@马克 @阿尔夫 我认为阿尔夫是正确的。您可以尝试将代理对写入控制台,看看会出现多少个字形。但是阿尔夫,没有必要对此如此激动! - David Heffernan
显示剩余6条评论

-1

只是为了明确起见,这里有些人提到了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;

1
Unicode不仅仅是两个字节,因为它不是一种编码而是一个字符集:UTF-8和Unicode之间的区别? - Salvador

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接