1

可悲的是,这是我本周第三次发布问题。

我必须使用 unicode 编码(或 UTF8)将文本写入文件。
这就是我所做的:

创建wofstream mystream;然后我wstring像这样放一个mystream << L"hello world";

第一个问题:在我的情况下,流使用什么样的编码?

其次,我想加载我的新文件,但是如何读取这些行?ifstream'sgetline不起作用,因为这条线显然被毁了。

4

2 回答 2

8

我认为“unicode encoding”是指 UTF-16。实际上有几种编码可能被称为 Unicode 编码,但大多数不熟悉 Unicode 的人认为它是指 UTF-16(我认为主要是因为微软在他们的所有文档中都犯了这个错误)。我的回答还假设您正在为 Windows 编写代码,因此您的内部数据是存储在 wchar_t 字符串中的 UTF-16。


使用宽流对象并不意味着文件输入或输出将使用宽字符完成。事实上,宽流将使用流区域设置的 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++ 生命周期管理不同。

构面是引用计数的,当具有特定构面的最后一个语言环境被销毁时,该构面将被删除,除非已通过使用 refs 参数构造构面来明确禁用该构面1。上面的示例代码将生命周期管理留给了语言环境,因此看起来类似于内存泄漏。但是,代码是正确的。就异常安全而言,唯一可能在成功分配和本地环境假定的已分配对象的所有权之间运行的代码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. 只需确保在方面超出范围之前销毁语言环境(以及每个副本)。

这是智能指针无法让事情变得更好的一次,因为将所有权移交给智能指针无视对象是很棘手的。您必须弄清楚如何手动处理在语言环境收到构面并因此获得所有权但在智能指针放弃所有权之前发生的异常。

于 2012-10-08T21:42:10.103 回答
1

wchar_t,支持wstream和的类型,wstring取决于平台:在 Windows 上为 2 个字节,在某些(全部?)Linux 上为 4 个字节。所以你最终会写出“Unicode”,但究竟哪个 Unicode 会受到许多变量的影响。你可能会写 UTF32/UCS4,你最终可能会使用 UTF16/UCS2。

如果您想使用特定的、受良好控制的编码(例如 UTF8 或 UCS-2LE 与 UCS-2BE 来控制字节序)进行编写,那么您需要像iconv这样的东西。您还可以使用std::localeimbue,请参阅https://stackoverflow.com/a/1275260/105929

于 2012-10-08T20:56:52.920 回答