0 为什么需要iostream
我们从一开始就一直在利用C++的输入输出在做着各种练习,输入输出是由iostream库提供的,所以讨论此标准库是有必要的,它与C语言的 stdio库不同,它从一开始就是用多重继承与虚拟继承实现的面向对象的层次结构,作为一个c++的标准库组件提供给程序员使用。

  iostream为内置类型对象提供了输入输出支持,同时也支持文件的输入输出,类的设计者可以通过对iostream库的扩展,来支持自定义类型的输入输出操作。

  为什么说要扩展才能提供支持呢?我们来一个示例。

#include <stdio.h> 
#include <iostream>
usingnamespace std; class Test
{
public:
Test(int a=0,int b=0)
{
Test::a=a;
Test::b=b;
}
int a;
int b;
};
int main()
{
Test t(100,50);
printf("%???",t);//不明确的输出格式
scanf("%???",t);//不明确的输入格式
cout<<t<<endl;//同样不够明确
cin>>t;//同样不够明确
system("pause");
}
 

由于自定义类的特殊性,在上面的代码中,无论你使用c风格的输入输出,或者是c++的输入输出都不是不明确的一个表示,由于c语言没有运 算符重载机制,导致stdio库的不可扩充性,让我们无法让printf()和scanf()支持对自定义类对象的扩充识别,而c++是可以通过运算符重 载机制扩充 iostream库的,使系统能能够识别自定义类型,从而让输入输出明确的知道他们该干什么,格式是什么。

  在上例中我们之所以用printf与cout进行对比目的是为了告诉大家,C与C++处理输入输出的根本不同,我们从c远的输入输出可以很明显看出是函数调用方式,而c++的则是对象模式,cout和cin是ostream类和istream类的对象。

1 iostream: istream 和 ostream

C++中的iostream库主要包含下图所示的几个头文件:

IOSstream 库
fstream iomainip
ios iosfwd
iostream istream
ostream sstream
streambuf strstream

  我们所熟悉的输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的,为了允许双向的输入/输出,由istream和ostream派生出了iostream类。

  类的继承关系见下图:

iostream库定义了以下三个标准流对象:

  1. cin,表示标准输入(standard input)的istream类对象。cin使我们可以从设备读入数据。
  2. cout,表示标准输出(standard output)的ostream类对象。cout使我们可以向设备输出或者写数据。
  3. cerr,表示标准错误(standard error)的osttream类对象。cerr是导出程序错误消息的地方,它只能允许向屏幕设备写数据。

  输出主要由重载的左移操作符(<<)来完成,输入主要由重载的右移操作符(>>)完成:

  1. >>a表示将数据放入a对象中。
  2. <<a表示将a对象中存储的数据拿出。

  这些标准的流对象都有默认的所对应的设备,见下表:

C++对象名 设备名称 C中标准设备名 默认含义
cin 键盘 stdin 标准输入
cout 显示器屏幕 stdout 标准输出
cerr 显示器屏幕 stderr 标准错误输出

上表中的意思表明cin对象的默认输入设备是键盘,cout对象的默认输出设备是显示器屏幕。

  那么原理上C++有是如何利用cin/cout对象与左移和右移运算符重载来实现输入输出的呢?

  下面我们以输出为例,说明其实现原理:

  1. cout是ostream类的对象,因为它所指向的是标准设备(显示器屏幕),所以它在iostream头文件中作为全局对象进行定义。
  2. ostream cout(stdout);//其默认指向的C中的标准设备名,作为其构造函数的参数使用。
  3. 在iostream.h头文件中,ostream类对应每个基本数据类型都有其友元函数对左移操作符进行了友元函数的重载。
    • ostream& operator<<(ostream &temp,int source);
    • ostream& operator<<(ostream &temp,char *ps);
    • ... 等等

  一句输出语句:cout<<"http://www.cppblog.com/andxie99";,事实上调用的就是 ostream& operator<<(ostream &temp,char *ps);这个运算符重载函数,由于返回的是流对象的引用,引用可以作为左值使用,所以当程序中有类似 cout<<"http://www.cppblog.com/andxie99"<<"白纸人生";这样的语句出现的时候,就 能够构成连续输出。

  由于iostream库不光支持对象的输入输出,同时也支持文件流的输入输出,所以在详细讲解左移与右移运算符重载之前,我们有必要先对文件的输入输出以及输入输出的控制符有所了解。

2 fstream: ifstream 和 ofstream

和文件有关系的输入输出类主要在fstream.h这个头文件中被定义,在这个头文件中主要被定义了三个类,由这三个类控制对文件的各种输入输出操 作,他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,他们之间的继承关系见下图所 示。

由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream.h头文件中是没有像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象,我们要以文件作为设备向文件输出信息(也就是向文件写数据),那么就应该使用ofstream类。

  ofstream类的默认构造函数原形为:

  ofstream::ofstream(constchar *filename,int mode = ios::out,
                      int penprot = filebuf::openprot);
 
  • filename:  要打开的文件名
  • mode:    要打开文件的方式
  • prot:    打开文件的属性

  其中mode和openprot这两个参数的可选项表见下表:

mode属性表
ios::app 以追加的方式打开文件
ios::ate 文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in 文件以输入方式打开
ios::out 文件以输出方式打开
ios::trunc 如果文件存在,把文件长度设为0

可以用“或”把以上属性连接起来,如ios::out|ios::binary。

openprot属性表
属性 含义
0 普通文件,打开访问
1 只读文件
2 隐含文件
4 系统文件

可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。

实例代码如下:

#include <fstream> 
usingnamespace std; int main()
{
ofstream myfile("c:\\1.txt",ios::out|ios::trunc,0);
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99";
myfile.close()
system("pause");
}
 

文件使用完后可以使用close成员函数关闭文件。

  ios::app为追加模式,在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。

  示例如下:

#include <iostream> 
#include <fstream>
usingnamespace std;
int main()
{
ofstream myfile("c:\\1.txt",ios::app,0);
if(!myfile)//或者写成myfile.fail()
{
cout<<"文件打开失败,目标文件状态可能为只读!";
system("pause");
exit(1);
}
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99"<<endl;
myfile.close();
}
 

  在定义ifstream和ofstream类对象的时候,我们也可以不指定文件。以后可以通过成员函数open()显式的把一个文件连接到一个类对象上。

  例如:

#include <iostream> 
#include <fstream>
usingnamespace std;
int main()
{
ofstream myfile;
myfile.open("c:\\1.txt",ios::out|ios::app,0);
if(!myfile)//或者写成myfile.fail()
{
cout<<"文件创建失败,磁盘不可写或者文件为只读!";
system("pause");
exit(1);
}
myfile<<"白纸人生"<<endl<<"网址:"<<"www.cppblog.com/andxie99"<<endl;
myfile.close();
}
 

下面我们来看一下是如何利用ifstream类对象,将文件中的数据读取出来,然后再输出到标准设备中的例子。

  代码如下:

#include <iostream> 
#include <fstream>
#include <string>
usingnamespace std;
int main()
{
ifstream myfile;
myfile.open("c:\\1.txt",ios::in,0);
if(!myfile)
{
cout<<"文件读错误";
system("pause");
exit(1);
}
char ch;
string content;
while(myfile.get(ch))
{
content+=ch;
cout.put(ch);//cout<<ch;这么写也是可以的
}
myfile.close();
cout<<content;
system("pause");
}
 

上例中,我们利用成员函数get(),逐一的读取文件中的有效字符,再利用put()成员函数,将文件中的数据通过循环逐一输出到标准设备(屏幕) 上, get()成员函数会在文件读到默尾的时候返回假值,所以我们可以利用它的这个特性作为while循环的终止条件,我们同时也在上例中引入了C++风格的 字符串类型string,在循环读取的时候逐一保存到content中,要使用string类型,必须包含string.h的头文件。

我们在简单介绍过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");
}
 

由于fstream类可以对文件同时进行读写操作,所以对它的对象进行初始话的时候一定要显式的指定mode和openprot参数。

  接下来我们来学习一下串流类的基础知识,什么叫串流类?

3 strstream: ostrstream 和 istrstream

简单的理解就是能够控制字符串类型对象进行输入输出的类,C++不光可以支持C++风格的字符串流控制,还可以支持C风格的字符串流控制。

  我们先看看看C++是如何对C风格的字符串流进行控制的,C中的字符串其实也就是字符数组,字符数组内的数据在内存中的位置的排列是连续的,我 们通常用 char str[size]或者char *str的方式声明创建C风格字符数组,为了能让字符数组作为设备并提供输入输出操作,C++引入了ostrstream、istrstream、 strstream这三个类,要使用他们创建对象就必须包含strstream.h头文件。

  • istrstream类用于执行C风格的串流的输入操作,也就是以字符串数组作为输入设备。
  • ostrstream类用于执行C风格的串流的输出操作,也就是一字符串数组作为输出设备。
  • strstream类同时可以支持C风格的串流的输入输出操作。

  istrstream类是从istream(输入流类)和strstreambase(字符串流基类)派生而来,ostrstream是从 ostream(输出流类)和strstreambase(字符串流基类)派生而来,strstream则是从iostream(输入输出流类)和和 strstreambase(字符串流基类)派生而来。

  他们的继承关系如下图所示:

  串流同样不是标准设备,不会有预先定义好的全局对象,所以不能直接操作,需要通过构造函数创建对象。

类istrstream的构造函数原形如下:

  istrstream::istrstream(constchar *str,int size);
 

参数1表示字符串数组,而参数2表示数组大小,当size为0时,表示istrstream类对象直接连接到由str所指向的内存空间并以\0结尾的字符串。

  下面的示例代码就是利用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::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");
}
 

上面的代码中,我们创建一个c风格的串流输出对象ostr,我们将arraysize内的数据成功的以字符串的形式输出到了ostr对象所指向的 pbuffer指针的堆空间中,pbuffer也正是我们要输出的字符串数组,在结尾要使用ends结束字符串,如果不这么做就有溢出的危险。

4 stringstream

对于stringstream了来说,不用我多说,大家也已经知道它是用于C++风格的字符串的输入输出的。  stringstream的构造函数原形如下:

  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");
}
 

除此而外,stringstream类的对象我们还常用它进行string与各种内置类型数据之间的转换。示例代码如下:

#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");
}
 

接下来我们来学习一下输入/输出的状态标志的相关知识.

5 io_state 输入/输出的状态标志

C++中负责的输入/输出的系统包括了关于每一个输入/输出操作的结果的记录信息。这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型(就像open_mode一样),以下便是它包含的值。

  • goodbit 无错误
  • Eofbit 已到达文件尾
  • failbit 非致命的输入/输出错误,可挽回
  • badbit 致命的输入/输出错误,无法挽回

有两种方法可以获得输入/输出的状态信息。一种方法是通过调用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");
}
 

如果错误发生,那么流状态既被标记为错误,你必须清除这些错误状态,以使你的程序能正确适当地继续运行。要清除错误状态,需使用clear()函数。此函数带一个参数,它是你将要设为当前状态的标志值。,只要将ios::goodbit作为实参。

  示例代码如下:

#include <iostream> 
usingnamespace std; int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
cin.clear(ios::goodbit);
cout<<cin.rdstate()<<endl;
system("pause");
}
 

通常当我们发现输入有错又需要改正的时候,使用clear()更改标记为正确后,同时也需要使用get()成员函数清除输入缓冲区,以达到重复输入的目的。

  示例代码如下:

#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");
}

C++的iostream标准库介绍+使用详解(转)的更多相关文章

  1. 《挑战30天C++入门极限》C++的iostream标准库介绍(3)

        C++的iostream标准库介绍(3) C语言提供了格式化输入输出的方法,C++也同样,但是C++的控制符使用起来更为简单方便,在c++下有两中方法控制格式化输入输出. 1.有流对象的成员函 ...

  2. 《挑战30天C++入门极限》C++的iostream标准库介绍(2)

        C++的iostream标准库介绍(2) 接下来我们继续看一下C++风格的串流控制,C++引入了ostringstream.istringstream.stringstream这三个类,要使用 ...

  3. 《挑战30天C++入门极限》C++的iostream标准库介绍(1)

        C++的iostream标准库介绍(1) 我们从一开始就一直在利用C++的输入输出在做着各种练习,输入输出是由iostream库提供的,所以讨论此标准库是有必要的,它与C语言的stdio库不同 ...

  4. c/c++ 标准库 bind 函数 详解

    标准库 bind 函数 详解 bind函数:接收一个函数名作为参数,生成一个新的函数. auto newCallable = bind(callbale, arg_list); arg_list中的参 ...

  5. c/c++ 标准库 插入迭代器 详解

    标准库 插入迭代器 详解 插入迭代器作用:copy等函数不能改变容器的大小,所以有时copy先容器是个空的容器,如果不使用插入迭代器,是无法使用copy等函数的. 例如下面的代码就是错误的: list ...

  6. C++标准库vector类型详解

    Vector简介 vector是定义在C++标准模板库,它是一个多功能.能够操作多种数据结构和算法的模板类(关于模板类我们后面会介绍,如何创建自己的模板类).vector是一个容器,能够像容器一样存放 ...

  7. 标准库bufio个人详解

    本文是我有通俗的语言写的如果有误请指出. 先看bufio官方文档 https://studygolang.com/pkgdoc文档地址 主要分三部分Reader.Writer.Scanner 分别是读 ...

  8. Python的标准库介绍与常用的第三方库

    Python的标准库介绍与常用的第三方库 Python的标准库: datetime:为日期和时间的处理提供了简单和复杂的方法. zlib:以下模块直接支持通用的数据打包和压缩格式:zlib,gzip, ...

  9. Rxjava2 介绍与详解实例

    目录 前言 RX介绍 Rx模式 Rx使用依赖: Rxjava的入门基础 1. Observable 2. Flowable 3. Single 4. Completable 5. Maybe 6. S ...

随机推荐

  1. google test框架与eclipse插件

    1. https://github.com/google/googletest    (google的测试框架) 2. eclipse测试框架插件 https://github.com/xgsa/cd ...

  2. Mongoose vs mongodb native driver – what to prefer?

      Paul Shan 7th Jun 2015 Mongoose or mongodb native driver, which one to use? This is one of the ini ...

  3. HDU 1541 Stars (线段树)

     Problem Description Astronomers often examine star maps where stars are represented by points on ...

  4. mysql下载安装使用教程

    今天花了点时间终于把mysql安装好了,哈哈!下面上干货! (1)从官网下载mysql.地址https://www.mysql.com/ 图:按照图上的步骤就能找到 a.首页点击    DOWNLOA ...

  5. GPUImage API文档之GPUImageContext类

    GPUImageContext类,提供OpenGL ES基本环境,我们一般不会用到,所以讲的很简单. 属性 @property(readonly, nonatomic) dispatch_queue_ ...

  6. 【转】web.xml不同版本的头

    web.xml v2.3 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web- ...

  7. iOS 真机上图标不显示

    今天在调试时发现模拟器上图标显示了.但真机上测试时发现图标不显示. 解决办法 57*57  的图标然后命名为: Icon.png 这样显示就正常了. 参考资料:http://www.cnblogs.c ...

  8. 论asp.net out、ref、return

    论asp.net out.ref.return ref(引用类型) ref引用类型进出都必须赋值,赋值后会改变类型原来的指针. out(值类型) out值类型进可以不赋值,出必须赋值. return( ...

  9. Nginx+Lua+Redis构建高并发应用

    一.  源文来自:http://www.ttlsa.com/nginx/nginx-lua-redis/ 二.  预览如下:

  10. QtWebkit包含的类简介

    前言 WebKit从Qt 4.4开始被作为一个Module被集成到Qt中.简单的说,Qt webkit就是Qt对Apple公司webkit的支持而开发的库,主要包括以下几个类: QWebDatabas ...