C++流混淆:istreambuf_iterator与istream_iterator有什么区别?

57
istreambuf_iteratoristream_iterator 的区别是什么? 并且一般而言,streams 和 streambufs 有什么不同? 我真的找不到任何清晰的解释,所以决定在这里提问。

2个回答

61

IOstreams使用streambuf作为它们的输入/输出源/目标。实际上,streambuf系列负责处理I/O操作,而IOstream系列仅用于格式化和字符串转换。

现在,istream_iterator采用一个模板参数来表示从streambuf中提取出的未经格式化的字符串序列应该被格式化为什么类型,例如istream_iterator<int>会将所有传入的文本(以空白字符分隔)解释为int

另一方面,istreambuf_iterator只关心原始字符,并直接迭代传递给它的istream相关联的streambuf。

通常情况下,如果您只关心原始字符,请使用istreambuf_iterator。如果您关心格式化的输入,请使用istream_iterator

以上所有内容也适用于ostream_iteratorostreambuf_iterator


通常情况下,如果您只对原始字符感兴趣,请使用istream_iterator,这应该是istreambuf_iterator吗? - Mankarse
2
只有一个小细节:iostream家族本身并不真正执行太多的格式化工作——这主要是委托给与流相关联的区域设置。 iostream基本上是一个“媒人”,将区域设置与streambuf组合在一起。 然而,大部分真正的工作都是委托给其中一个。 - Jerry Coffin
@Jerry:我不想深入探讨区域设置这种奇怪的东西,因为我自己也不太理解,所以就此打住了。 - Xeo
1
@Xeo:尽管我很想这样做,但我肯定不能责怪你! - Jerry Coffin
@Jerry:如果你愿意的话,可以在这个答案中添加本地化内容,或者自己写一个答案。;) - Xeo
@Xeo 很棒的答案,谢谢! - Gilad

23

这是一个非常不好保密的秘密: 实际上,iostream与在您的计算机上读写文件几乎没有任何关系。

iostream基本上只是作为流缓冲区和区域设置之间的“媒人”:

enter image description here

iostream存储了有关如何进行转换的一些状态(例如转换的当前宽度和精度)。 它使用这些状态指导区域设置如何在哪里进行转换(例如,在该缓冲区中将此数字转换为字符串,宽度为 8,精度为 5)。

虽然您没有直接询问,但区域设置实际上只是一个容器--但(出于奇怪性)是一种类型安全的异构容器。 它所包含的内容是facets。 facet对象定义整个locale的一个单独facet。 标准定义了许多facets,用于从读取和写入数字(num_get,num_put)到分类字符(ctype facet)的所有内容。

默认情况下,流将使用“C”区域设置。这很基础--数字只是以数字串的形式转换,它只将26个小写英文字母和26个大写英文字母识别为字母,等等。 但是,您可以使用您选择的不同locale imbue流。 您可以通过指定字符串中的名称选择要使用的区域设置。 特别有趣的一种是由空字符串选择的一种。 使用空字符串基本上告诉运行时库选择它“认为”最合适的区域设置,通常基于用户如何配置操作系统。 这使得代码能够以本地化格式处理数据,而无需明确编写任何特定的区域设置。

因此,istream_iterator和 istreambuf_iterator 之间的基本区别在于通过 istreambuf_iterator 输出的数据没有经过大部分语言环境执行的转换,但是通过 istream_iterator 输出的数据已经被语言环境转换。
就值得一提的是,在读取来自 istreambuf(通过迭代器或其他方式)的数据时,会执行一小部分基于语言环境的转换:除了各种“格式化”类型的事情之外,语言环境还包含一个 codecvt facet,它用于将某些外部表示形式转换为某些内部表示形式(例如,从UTF-8到UTF-32)。
忽略它们都存储在语言环境中可能更有意义,只考虑涉及到的单个facet:
enter image description here
因此,istream_iterator istreambuf_iterator 之间的真正区别在于对来自任一方的数据都执行了一点转换(至少存在潜在性),但是从 istreambuf_iterator 获得的数据进行的转换较少。

这就是我不想使用iostreams的原因,因为我不想涉及到codecvt。 - Pavel P
在我的情况下,我完全避免使用它们,我从不使用它们。我更喜欢printf而不是所有那些疯狂的尖括号和极其缓慢的性能。我们曾多次发现性能问题与iostream的使用有关。 - Pavel P
@Pavel:好的,如果你更喜欢使用printf,那就用它吧。如果你想要一个存储原始数据的流缓冲区,你当然也可以这样做(只是不太确定为什么你会问这个问题,如果你并不想要它的话)。 - Jerry Coffin
直接使用streambufs处理原始二进制数据怎么样,这是可能的吗?这意味着我想知道是否可以使用常规iostreams而不涉及codecvt并且不编写任何自定义流或类似的内容。因此,二进制的常规格式化输出至少与printf相似,而不是慢100倍。请注意,据我所知,在Linux / GCC上的iostream没有这些问题,而在Windows / VS上的iostreams存在这些问题。 - Pavel P
1
@Pavel:除非你做了什么极其错误的事情(例如,在只需要\n的地方使用std::endl),否则iostreams在速度上与C I/O相当竞争力。https://dev59.com/THI-5IYBdhLWcg3wTWZu#1926432 - Jerry Coffin
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接