iostream为内置类型对象提供了输入输出支持,同时也支持文件的输入输出,类的设计者可以通过对iostream库的扩展,来支持自定义类型的输入输出操作。
由于自定义类的特殊性,在上面的代码中,无论你使用c风格的输入输出,或者是c++的输入输出都不是不明确的一个表示,由于c语言没有运算符重载机制,导致stdio库的不可扩充性,让我们无法让printf()和scanf()支持对自定义类对象的扩充识别,而c++是可以通过运算符重载机制扩充 iostream库的,使系统能能够识别自定义类型,从而让输入输出明确的知道他们该干什么,格式是什么。
在上例中我们之所以用printf与cout进行对比目的是为了告诉大家,C与C++处理输入输出的根本不同,我们从c远的输入输出可以很明显看出是函数调用方式,而c++的则是对象模式,cout和cin是ostream类和istream类的对象。
IOSstream 库 | |
---|---|
fstream | iomainip |
ios | iosfwd |
iostream | istream |
ostream | sstream |
streambuf | strstream |
我们所熟悉的输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的,为了允许双向的输入/输出,由istream和ostream派生出了iostream类。
类的继承关系见下图:
iostream库定义了以下三个标准流对象:
输出主要由重载的左移操作符(<<)来完成,输入主要由重载的右移操作符(>>)完成:
这些标准的流对象都有默认的所对应的设备,见下表:
C++对象名 | 设备名称 | C中标准设备名 | 默认含义 |
---|---|---|---|
cin | 键盘 | stdin | 标准输入 |
cout | 显示器屏幕 | stdout | 标准输出 |
cerr | 显示器屏幕 | stderr | 标准错误输出 |
那么原理上C++有是如何利用cin/cout对象与左移和右移运算符重载来实现输入输出的呢?
下面我们以输出为例,说明其实现原理:
一句输出语句:cout<<"http://www.cppblog.com/andxie99";,事实上调用的就是ostream& operator<<(ostream &temp,char *ps);这个运算符重载函数,由于返回的是流对象的引用,引用可以作为左值使用,所以当程序中有类似cout<<"http://www.cppblog.com/andxie99"<<"白纸人生";这样的语句出现的时候,就能够构成连续输出。
由于iostream库不光支持对象的输入输出,同时也支持文件流的输入输出,所以在详细讲解左移与右移运算符重载之前,我们有必要先对文件的输入输出以及输入输出的控制符有所了解。
由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream.h头文件中是没有像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象,我们要以文件作为设备向文件输出信息(也就是向文件写数据),那么就应该使用ofstream类。
ofstream类的默认构造函数原形为:
ofstream::ofstream(constchar *filename,int mode = ios::out,
int penprot = filebuf::openprot);
其中mode和openprot这两个参数的可选项表见下表:
mode属性表 | |
ios::app | 以追加的方式打开文件 |
ios::ate | 文件打开后定位到文件尾,ios:app就包含有此属性 |
ios::binary | 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文 |
ios::in | 文件以输入方式打开 |
ios::out | 文件以输出方式打开 |
ios::trunc | 如果文件存在,把文件长度设为0 |
openprot属性表 | |
属性 | 含义 |
0 | 普通文件,打开访问 |
1 | 只读文件 |
2 | 隐含文件 |
4 | 系统文件 |
ios::app为追加模式,在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。
在定义ifstream和ofstream类对象的时候,我们也可以不指定文件。以后可以通过成员函数open()显式的把一个文件连接到一个类对象上。
我们在简单介绍过ofstream类和ifstream类后,我们再来看一下fstream类,fstream类是由iostream派生而来,fstream类对象可以同对文件进行读写操作。
示例代码如下:
#include <iostream> #include <fstream> usingnamespace std; int main() { fstream myfile; myfile.open("c://1.txt",ios::out|ios::app,0); if(!myfile) { cout<<"文件写错误,文件属性可能为只读!"<<endl; system("pause"); exit(1); } myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99"<<endl; myfile.close(); myfile.open("c://1.txt",ios::in,0); if(!myfile) { cout<<"文件读错误,文件可能丢失!"<<endl; system("pause"); exit(1); } char ch; while(myfile.get(ch)) { cout.put(ch); } myfile.close(); system("pause"); }
接下来我们来学习一下串流类的基础知识,什么叫串流类?
我们先看看看C++是如何对C风格的字符串流进行控制的,C中的字符串其实也就是字符数组,字符数组内的数据在内存中的位置的排列是连续的,我们通常用 char str[size]或者char *str的方式声明创建C风格字符数组,为了能让字符数组作为设备并提供输入输出操作,C++引入了ostrstream、istrstream、 strstream这三个类,要使用他们创建对象就必须包含strstream.h头文件。
istrstream类是从istream(输入流类)和strstreambase(字符串流基类)派生而来,ostrstream是从 ostream(输出流类)和strstreambase(字符串流基类)派生而来,strstream则是从iostream(输入输出流类)和和 strstreambase(字符串流基类)派生而来。
他们的继承关系如下图所示:
串流同样不是标准设备,不会有预先定义好的全局对象,所以不能直接操作,需要通过构造函数创建对象。
类istrstream的构造函数原形如下:
istrstream::istrstream(constchar *str,int size);
下面的示例代码就是利用istrstream类创建类对象,制定流输入设备为字符串数组,通过它向一个字符型对象输入数据。代码如下:
#include <iostream> #include <strstream> usingnamespace std; int main() { char *name = "www.cppblog.com/andxie99"; int arraysize = strlen(name)+1; istrstream is(name,arraysize); char temp; is>>temp; cout<<temp; system("pause"); }
ostrstream::ostrstream(char *_Ptr,int streamsize,int Mode = ios::out);
我们来一个示例代码:
#include <iostream> #include <strstream> usingnamespace std; int main() { int arraysize=1; char *pbuffer=newchar[arraysize]; ostrstream ostr(pbuffer,arraysize,ios::out); ostr<<arraysize<<ends;//使用ostrstream输出到流对象的时候,要用ends结束字符串 cout<<pbuffer; delete[] pbuffer; system("pause"); }
stringstream::stringstream(string str);
#include <iostream> #include <sstream> #include <string> usingnamespace std; int main() { stringstream ostr("ccc"); ostr.put('d'); ostr.put('e'); ostr<<"fg"; string gstr = ostr.str(); cout<<gstr<<endl; char a; ostr>>a; cout<<a system("pause"); }
#include <iostream> #include <sstream> #include <string> usingnamespace std; int main() { stringstream sstr; //--------int转string----------- int a=100; string str; sstr<<a; sstr>>str; cout<<str<<endl; //--------string转char[]-------- sstr.clear();//如果你想通过使用同一stringstream对象实现多种类型的转换,
//请注意在每一次转换之后都必须调用clear()成员函数。 string name = "colinguan"; char cname[200]; sstr<<name; sstr>>cname; cout<<cname; system("pause"); }
有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数,它将返回当前状态的错误标记。例如,假如没有任何错误,则rdstate()会返回goodbit.下例示例,表示出了rdstate()的用法:
#include <iostream> usingnamespace std; int main() { int a; cin>>a; cout<<cin.rdstate()<<endl; if(cin.rdstate() == ios::goodbit) { cout<<"输入数据的类型正确,无错误!"<<endl; } if(cin.rdstate() == ios_base::failbit) { cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<<endl; } system("pause"); }
bool bad();bool eof();bool fail();bool good();
下例示例,表示出了上面各成员函数的用法:
#include <iostream> usingnamespace std; int main() { int a; cin>>a; cout<<cin.rdstate()<<endl; if(cin.good()) { cout<<"输入数据的类型正确,无错误!"<<endl; } if(cin.fail()) { cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<<endl; } system("pause"); }
示例代码如下:
#include <iostream> usingnamespace std; int main() { int a; cin>>a; cout<<cin.rdstate()<<endl; cin.clear(ios::goodbit); cout<<cin.rdstate()<<endl; system("pause"); }
示例代码如下:
#include <iostream> usingnamespace std; int main() { int a; while(1) { cin>>a; if(!cin)//条件可改写为cin.fail() { cout<<"输入有错!请重新输入"<<endl; cin.clear(); cin.get(); } else { cout<<a; break; } } system("pause"); }
#include <iostream> #include <fstream> usingnamespace std; int main() { ifstream myfile("c://1.txt",ios_base::in,0); if(myfile.fail()) { cout<<"文件读取失败或指定文件不存在!"<<endl; } else { char ch; while(myfile.get(ch)) { cout<<ch; } if(myfile.eof()) { cout<<"文件内容已经全部读完"<<endl; } while(myfile.get(ch)) { cout<<ch; } } system("pause"); }