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



我一直在努力阅读iostreams并更好地理解它们。偶尔我发现它强调插入器(<<)和提取器(>>)意味着在文本序列化中使用。这是几个地方,但这篇文章是一个很好的例子:

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

<iostream>宇宙之外,存在<<和>>以类似流的方式使用,但不遵守任何文本约定。例如,当Qt的QDataStream:

使用时,它们编写二进制编码的数据:

http://doc.qt.nokia.com/latest/qdatastream.html细节

在语言层面,<<和>>操作符属于您的项目来重载(因此QDataStream所做的显然是可以接受的)。我的问题是,对于那些使用<iostream>的人来说,使用<<和>>运算符实现二进制编码和解码。是否有(例如)任何期望,如果写入磁盘上的文件,该文件应该是可见的和可编辑的文本编辑器?

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


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

将二进制数据(std::string)写入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的类是streambufs。但是请注意,一个合适的二进制文件是而不是,它只是转储内部原始数据的文件。就像文本文件一样(实际上甚至更多),二进制文件应该对它所包含的数据有一个明确指定的编码。特别是当文件需要在不同的系统上读取时。因此,格式化输出的概念对二进制文件也非常有意义;只是格式不同(例如,对于整数值,写入预先确定的字节数,首先写入最重要的字节)。

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

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

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

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

标准中iostreams的抽象是一个文本的抽象格式化的数据流;不支持任何非文本格式。这就是iostreams的抽象。这没什么不对的定义一个抽象为二进制格式的不同流类,但是在iostream中这样做可能会破坏现有的代码,而不是工作。

重载操作符>>和<<执行格式化IO。其余的IO函数(put、get、read、write等)执行未格式化的IO。未格式化IO意味着IO库只接受一个缓冲区,一个无符号字符序列作为其输入。此缓冲区可能包含文本消息或二进制内容。解释缓冲区是应用程序的责任。但是,格式化的IO将考虑区域设置。对于文本文件,根据应用程序运行的环境,在输入/输出操作中可能会发生一些特殊的字符转换,以使它们适应特定于系统的文本文件格式。在许多环境中,例如大多数基于unix的系统,以文本文件或二进制文件的形式打开文件没有区别。注意,可以重载操作符>>和<<适合你自己的类型。这意味着您能够将没有区域设置信息的格式化IO应用于您自己的类型,尽管这有点棘手。

相关内容

  • 没有找到相关文章

最新更新