我认为“unicode encoding”是指 UTF-16。实际上有几种编码可能被称为 Unicode 编码,但大多数不熟悉 Unicode 的人认为它是指 UTF-16(我认为主要是因为微软在他们的所有文档中都犯了这个错误)。我的回答还假设您正在为 Windows 编写代码,因此您的内部数据是 UTF-16 存储在 wchar_t 字符串中。
使用宽流对象并不意味着文件输入或输出将使用宽字符完成。事实上,宽流将使用流区域设置的 codecvt 方面,以便在流的字符类型 (wchar_t) 和 char 之间进行转换。
在 C++11 中,有一些 codecvt 方面可用于执行 UTF-16 或 UTF-8 输入/输出; codecvt_utf8, codecvt_utf16, codecvt_utf8_utf16.
codecvt_utf8 将在外部 UTF-8 多字节序列和内部 UTF-32/UCS4 或 UCS2 数据之间进行转换。 codecvt_utf16 将在外部 UTF-16 多字节序列和内部 UTF-32/UCS4 或 UCS2 数据之间进行转换。 codecvt_utf8_utf16 将在外部 UTF-8 多字节序列和内部 UTF-16 数据之间进行转换。
没有内置方法可以在外部 UTF-16 多字节序列和内部 UTF-16 数据之间进行转换,这是在内部使用 UTF-16 编码的 wchar_t 字符串和外部使用 UTF-16 编码的文件时所需要的.
但是由于您指出 UTF-8 输出是可以接受的,所以 codecvt_utf8_utf16 方面会很好地工作。
#include <fstream>
#include <codecvt>
int main() {
std::wofstream mystream("test.txt");
mystream.imbue(std::locale(std::locale(),
new std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::codecvt_mode(std::consume_header|std::generate_header)>));
mystream << "Hello, World!\n";
}
另请注意,此示例在 codecvt_utf8_utf16 方面设置选项以生成和读取所谓的“UTF-8 BOM”。这是微软猜测文件编码的惯例,在其他平台上通常不合适。
以下内容与手头的问题无关,但方面的生命周期管理与大多数其他现代 C++ 生命周期管理不同。
面是引用计数的,当具有特定面的最后一个语言环境被破坏时,面会被删除,除非已通过使用1 的 refs 参数构造面而特别禁用它。上面的示例代码将生命周期管理留给了语言环境,因此看起来类似于内存泄漏。但是,代码是正确的。就异常安全而言,唯一可能在成功分配和由语言环境假定的已分配对象的所有权之间运行的代码是表达式 std::locale(),它被声明为 noexcept。
另一种选择是使用不受区域设置管理的构面,并简单地确保它比区域设置和所有副本的寿命更长。使用具有静态存储持续时间的构面很简单,但请记住通过将其引用计数设置为 1 来指示语言环境不应删除构面。
static std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::codecvt_mode(std::consume_header|std::generate_header)> mycodecvt(1);
mystream.imbue(std::locale(std::locale(), mycodecvt));
如果语言环境仅在特定范围内短时间存在,那么您可以使用普通的局部变量。这与上述相同,但没有static。只需确保在构面超出范围之前销毁语言环境(以及每个副本)即可。
这是智能指针无法让事情变得更好的时候,因为将所有权移交给智能指针无视对象是很棘手的。您必须弄清楚如何手动处理在语言环境接收到构面并因此获得所有权但在智能指针放弃所有权之前发生的异常。