c++ - 插入器和提取器读取/写入二进制数据与文本

标签 c++ text iostream binary-data

我一直在尝试阅读 iostreams 并更好地理解它们。有时我发现它强调插入器( << )和提取器( >> )旨在用于文本序列化。这是几个地方,但这篇文章是一个很好的例子:

http://spec.winprog.org/streams/

<iostream>在某些情况下,<< 和 >> 以类似流的方式使用,但不遵守任何文本约定。例如,当 Qt 的 QDataStream 使用时,它们会写入二进制编码的数据。 :

http://doc.qt.nokia.com/latest/qdatastream.html#details

在语言级别,<< 和 >> 运算符属于您的项目要重载(因此 QDataStream 所做的显然是可以接受的)。我的问题是对于那些使用 <iostream> 的人来说,这是否被认为是一种不好的做法。使用 << 和 >> 运算符来实现二进制编码和解码。 (例如)是否有任何期望,如果写入磁盘上的文件,该文件应该可以使用文本编辑器查看和编辑?

是否应该始终使用其他方法名称并基于 read()write() ?或者应该将文本编码仅仅视为与标准库 iostream 集成的类可以选择忽略的默认行为?

更新 关于此的一个关键术语问题似乎是“格式化”与“未格式化”的 I/O 的区别(与术语“文本”与“二进制”相反)。我发现了这个问题:

writing binary data (std::string) to an std::ofstream?

它有来自@TomalakGeret'kal 的评论说:“无论如何,我不想将 << 用于二进制数据,因为我的大脑将其读取为“格式化输出”,这不是你在做什么。同样,它完全有效,但我不会像那样混淆我的大脑。”

该问题的公认答案说,只要您使用 ios::binary 就可以了.这似乎支持了辩论中“没有任何问题”的一面……但我仍然没有看到关于这个问题的任何权威来源。

最佳答案

其实运营商<<>>是位移运算符;将它们用于 I/O 严格来说已经是一种误用。然而,这种误用与运算符重载本身一样古老,而今天的 I/O 是它们最常见的用法,因此它们被广泛认为是 I/O 插入/提取运算符。我很确定如果没有 iostreams 的先例,没有人会使用这些运算符进行 I/O(尤其是在 C++11 中,它具有可变参数模板,解决了使用这些运算符为 iostreams 解决的主要问题,在一个更清洁的方式)。另一方面,从语言的角度来看,重载operator<<operator>>可以表示任何您希望他们表示的意思。

所以问题归结为这些运算符的可接受用途是什么。为此,我认为必须区分两种情况:第一,在 iostream 类上工作的新重载,第二,在其他类上工作的新重载,可能被设计为像 iostreams 一样工作。

让我们首先考虑 iostream 类上的新运算符。让我首先观察到 iostream 类都是关于格式化的(以及相反的过程,可以称为“解格式”;“词法分析”在这里不太合适,因为提取器不能确定类型,但只能尝试根据给定的类型解释数据)。负责原始数据的实际 I/O 的类是流缓冲。但是请注意,正确的二进制文件不是您只转储内部原始数据的文件。就像一个文本文件(实际上更是如此),一个二进制文件应该对它所包含的数据有一个明确指定的编码。特别是如果文件需要在不同的系统上读取。因此,格式化输出的概念对于二进制文件也非常有意义;只是格式不同(例如,为整数值写入预定数量的字节,最重要的字节在前)。

iostreams 本身是用于处理文本文件的类,也就是说,处理其内容被解释为数据的文本表示的文件。许多内置行为都为此进行了优化,如果在二进制文件上使用可能会导致问题。一个明显的例子是默认情况下在尝试任何输入之前跳过空格。对于二进制文件,这显然是错误的行为。此外,语言环境的使用对二进制文件没有意义(尽管有人可能会争辩说可能存在“二进制语言环境”,但我不认为为 iostream 定义的语言环境为此提供了合适的接口(interface))。因此我会说写二进制 operator<<operator>>对于 iostream 类将是错误的。

另一种情况是为二进制输入/输出定义一个单独的类(可能重用流缓冲层来执行实际的 I/O)。由于我们现在谈论的是不同的类,因此上面的论证不再适用。所以现在的问题是:应该operator<<operator>>在 I/O 上被视为“文本插入/提取操作符”或更一般地被视为“格式化数据插入/提取操作符”?标准类仅将它们用于文本,但是,根本没有用于二进制 I/O 插入/提取的标准类,因此标准用法无法区分两者。

我个人会说二进制插入/提取与文本插入/提取足够接近,这种用法是合理的。请注意,您还可以制作有意义的二进制 I/O 操纵器,例如bigendian , littleendianintwidth(n)确定要输出的整数的格式。

除此之外,还可以将这些运算符用于不是真正 I/O 的事情(并且您甚至不会想到使用 streambuf 层),例如从容器中读取或插入到容器中。在我看来,这已经构成了对运营商的滥用,因为那里的数据没有转换成或转换成不同的格式。它只是存储在一个容器中。

关于c++ - 插入器和提取器读取/写入二进制数据与文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8230786/

相关文章:

C++ 仿函数输出迭代器适配器

c++ - 在给定的迭代次数之后,使用RAII重新分配资源

c++ - 有一种方法可以区分标题类型吗?

c++ - 如何写一个指向 std::cerr 的指针?

c++ - For 循环未按预期执行

html - 在 CSS 中缩小页面?

csv - 根据列值拆分大型csv文本文件

unicode - 如何区分哪些 Unicode 字符是字母(单词),哪些是标点符号?

c# - 使用 TFS SDK 从 TFS 获取所有文件夹

c++ - 在密码提示中隐藏用户输入