IO库
<本章内容不多,复习完本章内容后打算先跳过第九章、第十章以及第十一章,先复习第十二章关于多态内存以及智能指针的知识,然后在将c++11新标准库容器(第九章顺序容器,第十一章关联容器)进行一个整合,然后学习一下第十章泛型算法,那么本书第二部分的关于c++标准库的内容就基本完成>
c++不直接处理输入和输出,而是使用标准库类来处理面向流的输入和输出:
1、iostream处理控制台IO;
2、fstream处理命名文件IO;
3、stringstream处理内存string的IO。
其中,fstream和stringstream类都是继承自iostream类,输入类继承istream,输出类继承ostream。所以在iostream类能够进行的操作,在fstreaam和stringstream类同样可以进行,而且fstream和stringstream类还各自定义了自己的操作。
一、iostream类
1、IO对象不能进行赋值或拷贝,因此对IO操作的函数形参不能设置为流类型,而是应该设置为引用类型,返回类型同样应该设置为引用类型,由于读写一个IO对象会改变其状态,因此形参不能为const。这就是为什么第十四章重载>>和<<运算符时,应该重载成以下形式:
istream& operator>>(istream &is, A& a)
{
is >> a.age >> a.name;
return is;
}
2、由于流可能处于错误状态,比如先一个int型变量输入整型时,却输入一个字符串,因此在使用一个流之前应该判断其是否处于一个良好状态,如:
int i;
while(cin>>i) //对输入流进行判断
cout<<i<<endl;
3、管理输出缓冲:每一个输出流都管理一个缓冲区,用来保存程序读写的数据,处于缓冲区的数据不一定立即被打印出来,操作系统可能将数据保存到缓冲区,随后再打印,这样也会提高系统性能,只有当缓冲区刷新时,缓冲区的数据才会被打印出来,那么什么时候缓冲刷新呢,一般以下几种情况会导致缓冲刷新使数据立马被打印出来:
①、程序正常结束,即main的return执行时,也会执行缓冲刷新;
②、缓冲区满时,会自动缓冲刷新,这样新数据才能进入缓冲区;
③、一个输出流关联到另一个流,比如读cin时会导致cout的缓冲区被刷新;
④、使用操作符unitbuf设置流内部状态,这样所有输出操作后都会立即进行缓冲刷新,如:
cout<<unitbuf; //设置流内部状态,所有输出操作后都会立即进行缓冲刷新
⑤、使用操作符如endl、flush、ends来显示刷新缓冲区。endl是我们经常使用的,endl进行换行然后刷新缓冲区。flush刷新缓冲区当不输出如何额外的字符;ends插入一个空字符然后刷新缓冲区。如:
cout<<"HELLO"<<ends; //输出为HELLOa,不知道为什么末尾有一个a
4、使用文件流对象:
1、当我们想要读写一个文件时,可以定义一个文件流对象,并且调用open函数将文件流对象和文件关联起来,一旦一个流对象已经打开,就不能在对其调用open函数。当读写完毕后,调用close关闭文件,如:
ifstream in; //构建一个ifstream流对象
ifstream if(filename1);//使用一个文件初始化流对象时,会隐式调用open进行关联,不用在调用open函数。
in.open(filename2) ;//将流对象与指定文件名关联起来
in.open(filename3);//错误,可以先调用in.close(),然后调用open();
2、注意ofstream为输出流对象,表示从内存中向存储器输出;ifstream为输入流对象,表示从存储器中向内存输入,fstream对象即可以表示输出也可以表示输入。此外我们还可以在open()函数中指明文件模式,文件模式有:
in 以读方式打开
out 以写方式打开
app 每次写操作前,均定位到文件末尾
ate 打开文件后立即定位到文件末尾
trnuc 截断文件
binary 以二进制方式进行IO
ifstream对象默认为in文件模式,ofstream默认为out文件模式,文件模式可以使用fstream::in或者ifstream::in如:
ifstream in;
in.open("filename")//默认为in文件模式,相当于in.open("filename",ifstream::in);
ofstream out;
out.open("filename")//默认为out文件模式,相当于out.open("filename",ofstream::in);
举个栗子,如果我们想要向D盘中的某个txt文档写入数据,相当于从内存向D盘输出数据,我们使用ofstream流对象:
#include<fstream>
ofstream out;
out.open("D:\\test.txt",ofstream::out); //如果D盘没有test.txt文档,那么就会创建一个文档
out << "床前明月光," << endl<< "疑是地上霜。" << endl << "举头望明月," << endl << "低头思故乡。" << endl;
out.close(); //关闭文档
测试,打开D盘找到test.txt文件并打开,里面的内容为:
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
注意,由于我们在每一句诗后面输入了操作符endl,所以test.txt文档中每句诗为换行。
另一个十分重要的问题是:当我们在使用应该ofstream流向test.txt输入数据时,原来test.txt中的内容会被丢弃,例如我们对上述文档test.txt在输入另一首诗:
ofstream out;
out.open("D:\\test.txt",ofstream::out);
out << "锄禾日当午," << endl<< "汗滴禾下土。" << endl << "谁知盘中餐," << endl << "粒粒皆辛苦。" << endl;
out.close();
此时打开D盘找到之前的test.txt,里面的内容只有:
锄禾日当午,
汗滴禾下土。
谁知盘中餐,
粒粒皆辛苦。
可以发现,之前输入的《静夜思》被丢弃了。为了防止ostream流清空给定文件,我们需要指定文件模式为app模式,即每次写操作前定位到文件末尾进行写入,如我们再对上述文件中写入《静夜思》:
ofstream out;
out.open("D:\\test.txt",fstream::app); //相对于 out.open("D:\\test.txt",fstream::app|fstream::out)
out << endl << "床前明月光," << endl << "疑是地上霜。" << endl << "举头望明月," << endl << "低头思故乡。" << endl;
out.close();
此时我们重新打开test.txt文档,里面的内容就是:
锄禾日当午,
汗滴禾下土。
谁知盘中餐,
粒粒皆辛苦。
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
注意:因为我在输入《静夜思》之前,先输入了一个endl,使用两首诗之间有空行。
4、类似,如果我们先把test.txt从D盘读入到内存并且打印到控制台窗口,就使用ifstream流对象,并且使用getline()函数读取每一行数据,如:
ifstream in; //从存储器读入到内存使用ifstream流对象
in.open("D:\\test.txt");//隐式文件模式是ifstream::in
string s;
while (getline(in, s)) //对每一次输入进行判断,当没有内容时循环结束
cout << s << endl;
由于我们需要向控制台输出数据,因此使用对象cout,getline函数表示从一个istream对象读取一整行数据并存入一个string对象,因此getline()函数包含两个参数,第一个是istream对象,也就是in,另一个是string对象,也就是s。在s拿到每一行数据后,再通过标准输出cout输出到控制台窗口。因此上述过程就是先从存储器读入数据,然后又将读入的数据写出到控制台窗口。我们的结果为: