2011-11-22 80 views
6

我一直在尝试阅读iostreams并更好地理解它们。偶尔我发现它强调插入器(<<)和提取器(>>)意在用于文本系列化。这几个地方,但这篇文章是一个很好的例子:插入器和提取器读取/写入二进制数据与文本

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

<iostream>宇宙中,存在其中< <和>>在流般的方式使用尚未做案件外不遵守任何文本惯例。例如,使用Qt的QDataStream当他们写的二进制编码数据:

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

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

应该一直使用其他方法名称并将它们基于read()write()?还是应该考虑文本编码仅仅是与标准库iostream集成的类可以选择忽略的默认行为?


UPDATE在这个关键术语的问题似乎是I/O被“格式化”与“格式化”(而不是术语“文本”与“二进制”)的区别。我发现这个问题:

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

它已经从@ TomalakGeret'kal说“我不想使用< <的二进制数据反正我的大脑把它读成‘格式化输出’评论这不是你正在做的事情,再次,它是完全有效的,但我不会像这样混淆我的大脑。“

该问题的接受答案表示,只要您使用ios::binary就可以。这似乎加强了“辩论中没有错”的一面......但我仍然没有看到有关这个问题的权威来源。

+1

“文本编码”是一个误导性的术语。我想说,“格式化I/O”更合适。 –

+0

不管你的框架做什么。 –

+0

@KerrekSB我比“格式化I/O”排除了什么“文本编码”排除了更清晰的内容。如果我有一个包含N个32位整数的对象,那么使用'write()'输出4个字节的N,然后是与这些值相对应的4 * N个字节...仍然是“格式化的”? – HostileFork

回答

9

其实运营商<<>>是位移运营商;严格地说,将它们用于I/O已经是一种误用。然而,这种滥用与运营商自身的重载程度一样大,而今天的I/O是它们最常用的用法,因此它们被广泛认为是I/O插入/提取操作符。我非常肯定,如果没有iostreams的先例,没有人会使用这些运算符进行I/O(特别是使用具有可变模板的C++ 11,解决了使用这些运算符解决iostreams的主要问题,更清洁的方式)。另一方面,从语言的角度来看,超载operator<<operator>>可以表示任何你想要他们的意思。

所以这个问题归结为什么将是一个可接受的使用这些运营商。为此,我认为必须区分两种情况:首先,新的重载工作在iostream类上;其次,新的重载工作在其他类上,可能设计为像iostream一样工作。

让我们考虑一下iostream类上的第一个新操作符。让我从观察iostream类都是关于格式化(和相反的过程,可以称为“deformatting”)的观察开始;“lexing”恕我直言,因为提取器不会确定类型,但只是尝试根据给定的类型来解释数据)。负责原始数据实际I/O的类是streambuf。但请注意,一个正确的二进制文件是而不是一个文件,您只需转储内部原始数据。就像一个文本文件(实际上更是如此),一个二进制文件应该包含一个指定的数据编码。特别是如果这些文件需要在不同的系统上读取。因此,对于二进制文件,格式化输出的概念也是非常有意义的;只是格式不同(例如,写一个预先确定数量的字节,其中最重要的字节先写入一个整数值)。

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

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

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

除此之外,还有那些不是真正I/O(甚至你甚至不会想到使用streambuf层)的东西,就像读取或插入容器一样。在我看来,这已经构成了运营商的滥用,因为那里的数据没有被翻译成不同的格式。它只是存储在一个容器中。

+0

谢谢你的冗长答案。在你提出的观点中,听起来好像你在暗示iostream插入器/提取器的“正确”使用会产生一个可以在不同的平台架构或编译器实现中传输的文件......并且具有相同的含义。有没有一个可靠的来源将其定义为“格式化”在这种情况下的含义?如果是这样,那么'<<' and '>>'以某种方式与该责任有唯一的联系(相对于只使用.read()和.write())的项目? – HostileFork

+1

@HostileFork:其实我会说一个正确的二进制文件有一个明确的格式;然后您可以从另一个平台读取它的事实是自动的。另外一个专门使用'.read()'和'.write()'的项目希望具有明确定义的二进制格式。这条规则的例外是一个临时文件,它有效地用作交换;这样的文件不会在当前进程中存活,也不会从另一个文件读取,因此它可能只包含内存转储。如果想要编写二进制文件的工具可以帮助用户编写正确的二进制文件,那显然是一个好主意。 – celtschk

+0

赏金获得详细回复。我对“答案”仍然有些不安,我可能会暂缓宣布,也许只是写我自己,因为我仍然觉得这里的水有点混浊。对于想要掌握iostream的新手们来说,很难真正提供指导,因为没有人上前承诺“这是好代码,这样做”或“这是不好的代码,不要这样做” 。 – HostileFork

4

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

+0

我的问题是基于承认'<<' and '>>'操作符属于您的项目过载(因此'QDataStream'正在做什么是可以接受的)。我想我的问题是更多想要实现iostream插入器和提取器的点对点的人说,如果写入磁盘上的文件,该文件应该可以通过文本编辑器查看和编辑... – HostileFork

+1

在iostream以外的流上使用'<<' and '>>'的二进制格式没有问题。问题正在改变它们在iostream上的含义。 –

+0

但是......说谁?请注意我上面的@KerrekSB的评论。他认为实现整数的'write()'不是“格式化的”......但是如果我校正了字节序并且写出了一个非常具体的4字节模式可以跨平台工作呢?在规范或规范中,我们可以发现它写的是对象不应该以这种方式与iostream的这些操作员序列化...? – HostileFork

3

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

+0

有趣的是,你提出了区域设置问题作为“格式化”定义中的重要区别。但我仍然试图明确一个“好”或“坏”的做法是什么。你认为你可以找到或创建一些示例代码来说明对比? – HostileFork

相关问题