我一直在尝试阅读 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
, littleendian
和 intwidth(n)
确定要输出的整数的格式。
除此之外,还可以将这些运算符用于不是真正 I/O 的事情(并且您甚至不会想到使用 streambuf 层),例如从容器中读取或插入到容器中。在我看来,这已经构成了对运营商的滥用,因为那里的数据没有转换成或转换成不同的格式。它只是存储在一个容器中。
关于c++ - 插入器和提取器读取/写入二进制数据与文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8230786/