【问题标题】:What is the different between printf & std::ostream under windows console using UTF-8 outputwindows控制台下使用UTF-8输出的printf和std::ostream有什么区别
【发布时间】:2012-04-29 12:00:11
【问题描述】:

我有一个将 UTF-8 字符串打印到控制台的程序:

#include <stdio.h>

int main()
{
    printf("Мир Peace Ειρήνη\n");
    return 0;   
}

我将控制台配置为使用 True Type 字体(Lucida 控制台),定义 UTF-8 代码页(chcp 65001)用 MinGW GCC 和 Visual Studio 2010 编译这个程序它完美地工作,我看到:输出:

Мир Peace Ειρήνη

我用std::cout做同样的事情

#include <iostream>

int main()
{
    std::cout << "Мир Peace Ειρήνη\n" ;
    return 0;   
}

使用 MinGW GCC 但使用 Visual Studio 2010 时,这完全可以正常工作 我得到方块,而不是方块(每个非 ASCII 字母两个)。

如果我使用重定向 test &gt;test.txt 运行程序,我会得到完美的 UTF-8 输出 在文件中。

两项测试均在 Windows 7 上完成。

问题:

  1. Visual Studio 标准库中的 printf 和 std::cout 在处理输出流方面有什么区别 - 显然其中一个有效而另一个无效?
  2. 如何解决这个问题?

真正的答案:

简而言之:你被搞砸了 - std::cout 并不能真正与 MSVC + UTF-8 一起工作 - 或者至少 需要付出巨大的努力才能使其行为合理。

长篇大论:阅读答案中引用的两篇文章。

【问题讨论】:

  • 将 unicode 直接嵌入源代码是不安全的 AFAIK。我相信最安全的方法是使用某种资源或使用 \u 和 u8 文字 (c++11) 输入 unicode 代码点
  • printf() 输出 unicode 和 std::cout �� 也是 Unicode problems in C++ but not C

标签: c++ winapi visual-c++ unicode


【解决方案1】:

你有许多有缺陷的假设,让我先纠正那些:

  • 看起来可以与 g++ 一起工作并不意味着 g++ 可以正常工作。

  • Visual Studio 不是编译器,它是一个支持多种语言和编译器的 IDE。

  • Visual C++ 的标准库需要修复的结论是正确的,但导致该结论的推理是错误的。还需要修复 g++ 标准库。更不用说 g++ 编译器本身了。

现在,Visual C++ 将 Windows ANSI(GetACP API 函数指定的编码)作为其未记录的 C++ 执行字符集。即使您的源代码是带有 BOM 的 UTF-8,窄字符串最终也会转换为 Windows ANSI。如果编译时在您的计算机上是包含所有非 ASCII 字符的代码页,则可以,否则窄字符串会出现乱码。因此,您的测试结果描述严重不完整,没有提及源代码编码和您的 Windows ANSI 代码页是什么。

但无论如何,“如果我使用重定向 test &gt;test.txt 运行程序,我会在文件中获得完美的 UTF-8 输出”表明您面临的是来自 Visual C++ 运行时的一点 C++ 级帮助,其中它绕过流输出并使用直接控制台输出,以便在控制台窗口中显示正确的字符。

当它的假设(例如 Windows ANSI 编码的窄字符串文字)不成立时,此帮助会导致垃圾。

这也意味着当您重定向流时效果会神秘消失。然后运行时库检测到流转到文件,并关闭直接控制台输出功能。不能保证您随后会获得原始的原始字节值,但显然您做到了,这很不幸,因为它掩盖了问题。

顺便说一句,Windows 控制台中的代码页 65001 在实践中是不可用的。许多程序只是崩溃。包括例如more.


获得正确输出的一种方法是直接使用 Windows API 级别,直接控制台输出。

使用 C++ 流获得正确的输出要复杂得多。

它太复杂了,在这里没有描述(正确!)的余地,所以我不得不向您推荐我关于它的两部分博客文章系列:part 1part 2

【讨论】:

  • 有道理。但这如何解释程序输出 squares 的 OP 问题?我希望 UTF-8 字节的控制台表示形式:对于俄语 М (U+0419),在我的机器上是 \xD0 \x99 或 ´╗
  • 字符串是 UTF-8(检查过,真的)我知道整个 MSVC/UTF-8 问题(废话)。我知道要正确处理它(原始源 UTF-8 没有 BOM 然后 char * 得到正确的 utf-8 当然 L"שלום" 搞砸了,但这是不同的故事,我可以用 "\xXY" 文字做同样的事情,结果是一样的;关于假设,基本假设是std::cout &lt;&lt; str; 的行为应该与puts(str) 相同这是假设,gcc 做到了这一点——至少可以预测。现在我清楚地明白std::cout 使用一些控制台 API这使得问题更加严重(TBC...)
  • 因为这不是真正的预期。最后我找到了你的(第 2 部分)文章,这篇 blogs.msdn.com/b/michkap/archive/2008/03/18/8306597.aspx Kaplan 的文章和这篇错误报告 connect.microsoft.com/VisualStudio/feedback/details/431244/…。最后,唯一合理的“解决方案”是创建我自己的流缓冲区。当 1/2 的应用程序不能很好地处理 Unicode 时,这是关于完全 CRAPPY windows Unicode 模型的又一个例子。
  • @Artyom 问题不在于 cout 使用特殊的控制台 API。 puts() 有效,而 cout 无效,因为 puts 一次将整个字节序列传递给控制台。控制台获取该数据,咨询 ConsoleOutputCP 以找出使用什么编码将其转换为 UTF-16,然后继续其愉快的方式。另一方面, cout 必须一次传递一个字节。控制台获取每个字节并尝试将其从 UTF-8 转换为 UTF-16。这将失败并导致 U+FFFD 显示在控制台上。简而言之,问题只是 Windows 上的控制台模型很笨。
猜你喜欢
  • 2010-12-12
  • 1970-01-01
  • 2011-04-26
  • 2010-09-08
  • 1970-01-01
  • 2011-05-13
  • 2012-09-20
  • 2015-11-26
  • 2013-12-16
相关资源
最近更新 更多