在 C++11 中读取/写入/打印 UTF-8

Posted

技术标签:

【中文标题】在 C++11 中读取/写入/打印 UTF-8【英文标题】:Reading/writing/printing UTF-8 in C++11 【发布时间】:2013-03-06 13:36:08 【问题描述】:

我一直在探索 C++11 的新 Unicode 功能,虽然 other C++11 encoding questions 非常有帮助,但我对来自 cppreference 的以下代码 sn-p 有疑问。代码写入然后立即读取以 UTF-8 编码保存的文本文件。

// Write
std::ofstream("text.txt") << u8"z\u6c34\U0001d10b";

// Read
std::wifstream file1("text.txt");
file1.imbue(std::locale("en_US.UTF8"));
std::cout << "Normal read from file (using default UTF-8/UTF-32 codecvt)\n";
for(wchar_t c; file1 >> c; ) // ?
   std::cout << std::hex << std::showbase << c << '\n';

我的问题很简单,为什么for 循环中需要wchar_t?可以使用简单的char * 声明u8 字符串文字,并且UTF-8 编码的位布局应该告诉系统字符的宽度。似乎有一些从 UTF-8 到 UTF-32 的自动转换(因此是 wchar_t),但如果是这种情况,为什么需要进行转换?

【问题讨论】:

这取决于很多事情。值得注意的是,即使不是不可能在控制台应用程序中使用 Windows,正确的 UTF8 行为也非常困难(需要至少大量非标准 API 调用 IIRC) wchar_t 被使用,因为wifstream 被使用,wifstream 执行你提到的“一些自动转换”。我的意思是展示自动转换(针对一个特定平台实现)与codecvt_utf8_utf16 提供的显式、可移植、独立于语言环境的Unicode 转换之间的区别。 【参考方案1】:

您使用wchar_t 是因为您正在使用wifstream 读取文件;如果您使用ifstream 阅读,您将使用char,同样适用于char16_tchar32_t

假设(如示例那样)wchar_t 是 32 位的,并且它所代表的本机字符集是 UTF-32 (UCS-4),那么这是最简单的以 UTF-32 格式读取文件的方法;它在示例中如此呈现,以与以 UTF-16 格式读取文件形成对比。一种更便携的方法是显式使用 basic_ifstream&lt;char32_t&gt;std::codecvt_utf8&lt;char32_t&gt;,因为这样可以保证从 UTF-8 输入流转换为 UTF-32 元素。

【讨论】:

+1,我写了那个例子,对比就是我想要的。 啊,我明白了!因此,始终将 UTF-8 显式转换为更宽的 wchar_t 是更好的做法,还是使用 ifstream 将原始 UTF-8 字节提取到本机 char 数组中仍然可以接受?我不确定是否从@Cubbi 的示例中推断出后者是不好的做法,或者它是否超出了示例的范围。 @PLPiper 是的,您始终可以将文件具有的任何多字节编码读入 char 数组,而无需进行任何转换。在标准 C++ 中,这样的数组并不能做很多事情(除了先转换为宽),但是很多库都采用 utf8 输入。【参考方案2】:

您使用的 cppreference 代码 sn-p 的想法是展示如何将 UTF-8 文件读入 UTF-16 字符串,这就是他们使用 ofstream 写入文件但使用 wifstream 读取文件的原因(因此 wchar_t )。

【讨论】:

以上是关于在 C++11 中读取/写入/打印 UTF-8的主要内容,如果未能解决你的问题,请参考以下文章

Unicode (UTF-8) 在 Python 中读取和写入文件

在VS上用C#读取和写入希伯来字符串

使用 utf-8 编码写入和读取文件

从 mysql 表中读取 utf-8 内容

c++11 多读取器/多写入器队列使用原子用于对象状态和永久递增索引

字节流读取和写入文件中中文