前言
虽然iostream类型不是容器,但标准库定义了可以用于这些IO类型对象的迭代器。
一、istream_iterator和ostream_iterator迭代器简介
istream_iterator读取输入流,ostream_iterator向一个输入流写数据.这些迭代器将它们对应的流当做一个特定类型的元素序列来处理.通过使用流迭代器,我们可以用泛型算法从流对象读取数据以及向其写入数据.
二、迭代器使用方法和注意事项
1.istream_iterator操作
注意: 当创建一个流迭代器时,必须指定迭代器将要读写的对象类型。
- 一个istream_iterator使用>>来读取流。因此,istream_iterator要读取的类型必须定义了输入运算符。
- 当创建一个istream_iterator时,我们可以将它绑定到一个流。
- 当然,我们还可以默认初始化迭代器,这样就创建了一个可以当做尾后值使用的迭代器
代码如下:
istream_iterator<int> int_it(cin);//从cin读取int
istream_iterator<int> int_eof;//尾后迭代器
ifstream in("afile");//打开一个aflie的文件
istream_iterator<string> str_it(in);//从"afile"读取字符串
下面是一个用istream_iterator从标准输入读取数据,存入一个vector的例子:
istream_iterator<int> int_iter(cin);//从cin读取int
istream_iterator<int> eof;//istream尾后迭代器
vector<int> vec;
while (int_iter != eof)//当有数据可供读取时
{
//后置递增运算符,返回迭代器的旧值
//解引用迭代器,获得从流读取的前一个值
vec.push_back(*int_iter++);
}
此循环从cin读取int值,保存在vec中.
在每个循环步中,循环体代码检查in_iter是否等于eof.
eof被定义为空的istream_iterator,从而可以当做尾后迭代器来使用.
对于一个绑定到流的迭代器,一旦其关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相等
对于传递给push_back的参数,其中用到了解引用运算符和后置递增运算符。
后置递增运算会从流中读取下一个值,向前推进,但返回的迭代器时迭代器的旧值。迭代器的旧值包含了从流中读取的前一个值,对迭代器进行解引用就能获得此值
注意: 后置递增运算会从流中读取下一个值
-
如果没有后置递增运算,那么读取一次后就会停止,我们可以看一下测试图:
-
我们可以将程序重写为如下形式,体现istream-iterator更有用的地方:
istream_iterator<int> int_iter(cin), eof;//从cin中读取int
vector<int> vec(int_iter,eof);//从迭代器范围构造vec
- 在本例中我们用一对表示元素范围的迭代器来构造vec。
- 这两个迭代器是istream_iterator,这意味着元素的范围是通过关联的流中读取数据获得的。
- 这个构造函数从cin中读取数据,直至遇到文件尾或者遇到一个不是int的数据类型为止.
- 从流中读取的数据被用来构造vec。
istream_iterator操作 | |
---|---|
istream_iterator< T > in(is) | in从输入流is读取类型为T的值 |
istream_iterator< T > end | 读取类型为T的值的istream_iterator迭代器,表示尾后的位置 |
in1==in2 | in1和in2必须读取相同的类型。如果他们都是尾后迭代器,或绑定到相同的输入,则两者相等 |
in1!=in2 | 相等的条件不满足则为不等 |
*in | 返回从流中读取的值 |
in->mem | 与(*in).mem的含义相同 |
++in,in++ | 使用元素类型所定义的>>运算符从输入流中读取下一个值。与以往一样,前置版本返回一个指向递增后的迭代器的引用,后置版本返回旧值 |
使用算法操作流迭代器
- 因为算法使用迭代器来操作处理数据,而流迭代器又至少支持某些算法来操作迭代器
istream_iterator<int> in(cin), eof;
cout << accumulate(in, eof, 0) << endl;
istream_iterator可以使用懒惰求值
- 当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据。
- 具体实现可以推迟从流中读取数据,直到我们使用迭代器时才真正读取。
- 标准库中的实现所保证的是,在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成了。
- 对于大多数程序员来说,立即读取还是推迟读取没什么差别。但是,如果我们创建一个istream_iterator,没有使用就销毁了,或者我们正在从两个不同的对象同步读取一个流,那么何时读取可能就很重要了。
2.ostream_iterator操作
- 我们可以对任何具有输出运算符(<<运算符)的定义ostream_iterator.
- 当创建一个ostream_iterator时,我们可以提供(可选的)第二参数,它是一个字符串,在输出每个元素后都会打印此字符串。此字符串必须是一个c风格的字符串(即,一个字符串字面常量或者指向一个空字符结尾的字符数组指针)。
- 必须将ostream_iterator绑定到一个指定的流,不能够存在空的或表示尾后位置的ostream_iterator
ostream_iterator操作 | |
---|---|
ostream_iterator< T > out(os) | out将类型为T的值写到输出流os中 |
ostream_iterator< T > out(os,d) | out将类型为T的值写到输出流os中,每个值后面都输出一个d。d指向一个空字符结尾的字符数组 |
out=val | 用<<运算符将val写入到out所绑定的ostream中,val的类型必须与out可写的类型兼容 |
*out,++out,out++ | 这些运算符是存在的,但不对out做任何事。每个运算符都返回out |
- 我们可以用ostream_iterator来输出值的序列:
代码如下:
vector<int> vec(10, 1);
ostream_iterator<int> out_iter(cout, " ");
for (auto e : vec)
*out_iter++ = e;//赋值语句实际上将元素写到cout
cout << endl;
- 此程序将vec中的每个元素写到cout,每个元素后加一个空格。
- 每次向out_iter赋值时,写操作就会被提交
- 值得注意的是,当我们向out_iter赋值时,可以忽略解引用和递增运算。即,循环可以重写成下面的样子:
vector<int> vec(10, 1);
ostream_iterator<int> out_iter(cout, " ");
for (auto e : vec)
out_iter= e;//赋值语句实际上将元素写到cout
cout << endl;
注意: 运算符*和++实际上对ostream_iterator对象不做任何事情,因此忽略它们对我们的程序没有任何影响。
但是,推荐第一种写法,在这种写法中,流迭代器的使用和其他迭代器的使用保持一致。如果想要将此循环改为操作其他迭代器的类型,修改起来非常容易.而且,对于读者来说,此循环的行为也更为清晰。
- 可以调用copy来打印vec中的元素,这比编写循环更为简单:
vector<int> vec(10, 1);
ostream_iterator<int> out_iter(cout, " ");
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
总结
以上就是今天要讲的内容,但是额外补充一点,我们还可以使用流迭代器处理类类型,前提是类型定义了>>和<<运算符。
转载:https://blog.csdn.net/m0_53157173/article/details/116405228