




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
8.1I/O标准流类 8.2格式化操作
8.3ios类的成员函数
8.4流操作算子
8.5文件流类
8.6串流类
8.7异常处理
本章要点
练习
8.1.1标准设备流
在C/C++中将设备看做对象,并分别给出了不同的关键字来标识设备,如表8-1所示。标准流的设备名是C++中预先定义的全局对象名。用I/O流的标准头文件iostream就可将其包含进来。8.1I/O标准流类表8-1标准设备
C++的输入/输出以流的形式实现。在输入操作中,流的传递方向是设备→内存;在输出操作中,流的传递方向是内存→设备。输入操作的运算符是>>,输出操作的运算符是<<。
在C++中将左移(<<)和右移(>>)重载为输出和输入运算符,并对“<<”和“>>”赋予了新的含义,分别称为流插入与流提取。
在C++中为输入/输出流各建立了一个队列结构缓冲区。每次的输出操作都是将数据插入到队尾,每次的输入操作都是从队首取数据。输出数据先插入到输出流队列的尾部,称为插入,用“<<”运算符表示。输入数据就是从输入流队列中提取,用“>>”运算符表示。8.1.2I/O流类
I/O相关的类及继承关系参见图8-1。图8-1流类的继承关系基类ios类是一个虚基类,它提供了对流进行格式化输入/输出操作以及错误处理的函数。ios类的派生类istream和ostream分别提供对流进行输入/输出操作的成员函数。iostream复合继承了istream和ostream类,它具有这两个类的行为,因此在大多数C++程序中都包含iostream这个头文件,其中包含了大量的输入与输出所需的信息。
istream类的派生类ifstream包含了对文件执行输入操作的成员函数,ostream类的派生类ofstream包含了对文件执行输出操作的成员函数。fstream继承iostream类,它同时具有输入与输出行为。在C++中,若要对文件进行操作,都要包含fstream这个头文件。8.1.3流对象及操作过程
cout是ostream流类的对象。它在iostream头文件中定义为:
ostreamcout(stdout);
它是以标准输出设备名作为其构造函数的参数,这样就将cout与显示器关联。若有cout<<x;,则将x单元内的值插入到名为cout的输出流中,即送给显示器。
ostream流类对应的每个基本数据类型都有重载函数,它们在ostream中这样声明:
ostream&operator<<(ostream&dest,char*pSource);
ostream&operator<<(ostream&dest,intsource);
ostream&operator<<(ostream&dest,charsource);
ostream&operator<<(constvoid*); //等等
同理,cin是istream流类的全局对象,istream流类也有若干个重载函数:
istream&operator>>(istream&dest,char*pSource);
istream&operator>>(istream&dest,intsource);
istream&operator>>(istream&dest,charsource); //等等在标准类库中提供了许多这样的重载流插入或流提取运算符的函数,在此仅列出几个。读者要想进一步了解,可参考C++系统的类库指南。
在C++中对于一些cout<<的操作都通过匹配重载函数来实现。若有:cout<<"IlearnC++";,由于“<<”是操作符,它的左边是ostream类的对象cout,右边是char*类型,即字符串,因此自动匹配ostream&operator<<(ostream&dest,char*pSource)重载函数。若有cout<<"thisis"<<8;,因运算符“<<”具有左结合性,所以可以看做为:(cout<<"thisis")<<8;。由于cout<<"thisis"返回对象的引用,因此得出cout<<"thisis"的类型与cout的相同,即都是ostream。那么该引用再与后面的8又自动匹配了另一个ostream&operator<<(ostream&dest,intsource);操作符,结果构成了可连续(输出)插入。在C++中对每一种内部类型数据的输出都有重载函数,因此用cout<<输出内部类型数据时,无论内部类型是什么,它都能自动寻求匹配重载函数。在需要时,用户还可以通过重载流插入与流提取运算符设计自己的输入/输出操作。
【例8-1】通过重载使流插入运算符作用于自定义对象。
#include<iostream>
#include<string>
usingnamespacestd;
classStudent
{public: Student(inta,strings,doubleb)
{name=s;num=a;score=b;}
staticinti;
stringname;
intnum;
doublescore;
};
intStudent::i=0;ostream&operator<<(ostream&str,StudentObj)
{Student::i++;
str<<Student::i<<“:\t”<<Obj.num<<‘\t’<<O<<‘\t’<<Obj.score<<endl;
returnstr;
//返回已插入数据的流对象
}
voidmain()
{Studenta(11,"Ally",3),b(22,"Halevy",4),c(33,"Jonas",5);
cout<<"\tNo"<<'\t'<<"Name"<<'\t'<<"Score"<<endl;
cout<<a<<b<<c<<endl;//该句的输出格式由重载函数决定
}运行结果:
No Name Score
1: 11 Ally 3
2: 22 Halevy 4
3: 33 Jonas 5
从结果来看,通过重载指定了用户要求的格式。有了这些重载函数,可以将标准设备流对象cout、cerr和clog以及用户自定义的流对象一起使用。8.2.1格式控制
在此前的程序中已使用了输入与输出,但都用的是默认方式,没有对输入与输出实现有效的控制。默认的输出格式是:十进制数,右对齐,6位有效位(不包括小数点)。在输出浮点数时,若整数部分位数超过6位,则用科学记数法输出;若整数部分位数小于或等于6位,则用浮点数法输出;当输出数据的幂小于-4时,用科学记数法输出。而实际应用中仅靠这些默认格式是不够的,往往对输入与输出格式有新的要求。8.2格 式 化 操 作将输入与输出中对数据格式的要求用一个长整型数表示,称为格式状态志或格式标志。在C++中对输入和输出的格式控制提供了两种方法,即用ios类的成员函数和用流操作算子,将方法与格式状态志结合起来,可实现对输入和输出的有效控制。8.2.2格式状态志
在ios类中有longx_flags;,它是受保护的成员,要用成员函数才可访问。x_flags低16位中的每一位的状态都表示着一种控制方式,因此将它称为格式状态志。由于直接记忆标志中的某一位对应某一状态不方便,因此用枚举符号常量定义了x_flags的每一位状态标志。x_flags中的某位为1,则相应的特性有效;某位为0,则相应的特性无效。在ios中定义的数据格式化标志如下:
enum //定义了一个无名枚举类型
{skipws=0x0001,
//忽略输入流中的空白(如空格、制表符、回车、换行等)
left=0x0002, //左对齐
right=0x0004, //右对齐
internal=0x0008, //符号左,数值右对齐oct=0x0020, //置基数为八进制,以八进制形式输入/输出
hex=0x0040,
//十六进制,只对整数有用,以十六进制形式输入/输出
showbase=0x0080,
//输出数制的基数,如输出2A时将输出0x2A
showpoint=0x0100, //输出小数点和尾数中的0
uppercase=0x0200, //十六进制数大写输出
showpos=0x0400, //强制打印+(正)号,只对十进制起作用
scientific=0x0800,
//科学记数法,浮点数用科学记数(指数)法表示
fixed=0x1000, //定点浮点数输出,小数点后占六位
unitbuf=0x2000, //每次插入后刷新所有流
};8.3.1ios类和ostream类的成员函数
ios类中提供了一些公有成员函数。因为cout是ostream类的对象,所以它可调用ostream类和ios类的成员函数。8.3ios类的成员函数
1.主要函数的功能
1) ios::setf
setf用于设置格式标志。它的功能是设置新格式标志,返回原格式标志。它的函数原型:
longsetf(longlFlags);
longsetf(longlFlags,longlMask);
当格式标志是表8-2中的第一个参数时,需要提供第二个参数。而且,第一个参数是否设置成功依赖于第二个参数。表8-2参数表
2) ios::unsetf
unseft用于取消格式标志。它的功能是取消格式标志,并返回被取消的格式标志。它的函数原型:
longunsetf(longlFlags);
3) ios::flags
flags用于设置格式标志或获取当前格式标志的状态。它的函数原型:
longflags()const;
longflags(longlFlags);若flags函数参数为空,则该函数用于获取当前格式标志的状态。若flags函数给出参数,则该函数用于设置格式标志。在设置格式标志时,该函数能设置新标记,清除旧标记,并返回旧标记。
4) ios::precision
precision用于设置输出精度,即总有效位数。它的函数原型:
intprecision()const;
intprecision(intnp);默认精度为6位,不含小数点。该函数的功能是获得原输出精度和设置新输出精度。精度设置后可以保留,直到下次重新设定。若已指定为scientific或fixed格式,然后设置精度,那么该精度值就是小数点后的位数。
5) ios::fill
fill用于设置填充字符。它的函数原型:
charfill()const;
charfill(charcFill);该函数的功能是获得原填充字符和设置新填充字符。默认的填充字符是空格。填充字符设置后可以保留,直到下次重新设定。
6) ios::width
width用于设置域宽。它的函数原型:
intwidth(intnw);
intwidth()const;
该函数的功能是设置新的最小域宽,返回原域宽。最小域宽是指当实际值大于设定值时能自动突破。域宽设置后不能保留,仅一次性有效。默认域宽由实际数据位数决定。
7) ostream::put
put用于输出一个字符。它的函数原型:
ostream&put(charch);
该函数的功能是将字符输出到ostream流类对象(显示器)上。
8) ostream::write
write用于无格式输出。它的函数原型:
ostream&write(constchar*pch,intnCount);
该函数的功能是将给出的字符串以及指定字符个数送给显示器。
2.应用举例
因为cout就是ostream流类对象之一,所以在此我们以它为例来了解一些ostream流类成员函数的使用方法。
【例8-2】演示格式控制。
#include<iostream>
#include<iomanip>
usingnamespacestd;
voidmain(){cout<<“|”<<123<<“|\n”; //以默认方式输出|123|
cout<<“|”<<123.456<<“|\n”; //输出|123.456|
cout.setf(ios::fixed,ios::floatfield); //设置fixed方式
cout<<“|”<<123.456<<“|\n”; //输出|123.456000|
cout.setf(ios::showpos); //设置showpos方式
cout.setf(ios::scientific,ios::floatfield);
//设置scientific方式cout<<“|”<<123<<“|\n”; //以showpos与scientific方式输出|+123|
cout<<“|”<<123.456<<“|\n”; //输出|+1.234560e+002|
cout.unsetf(ios::showpos|ios::scientific);
//同时取消showpos和scientific方式
cout.setf(ios::showbase|ios::uppercase); //设置基数标记,如0X
cout.setf(ios::hex,ios::basefield); //设置十六进制数
cout<<“|”<<123<<“|\n”;
//以hex|showbase|uppercase方式输出|0X7B|
cout<<“|”<<123.456<<“|\n”;
//输出|123.456|。非十进制数只对整数有效
cout.unsetf(ios::hex);
//取消hex。注:showbase|uppercase未取消
cout<<"|";cout.width(15); //域宽设置后仅一次性有效
cout<<123<<“|\n”;
//默认方式,输出|123|
cout<<"|"<<123.456<<"|\n"; //默认方式,输出|123.456|
cout.fill('#'); //设置填充,可保留
cout.setf(ios::left,ios::adjustfield); //设置左对齐
cout<<"|";cout.width(15);
//对于填充要给出宽度,否则无法填充
cout<<123<<“|\n”; //输出|123############|
cout.unsetf(ios::left); //取消左对齐,默认为右对齐
longformat=cout.flags(ios::hex|ios::showbase|ios::scientific);
//设置新标志,保存原标志
cout<<“|”<<123<<“|\n”;
//输出|0x7b|
cout<<“|”<<123.456<<“|\n”;
//输出|1.2346e+002|scientific方式,精度为4
cout.flags(format);//恢复原保存在format中的格式
}当scientific与fixed均不设置时,编译器自动选择一种合适的方式输出数据,即当幂小于-4或大于默认precision精度时,采用scientific科学记数法,否则采用fixed普通法。
【例8-3】演示precision与scientific或fixed的配合使用。
#include<iostream>
usingnamespacestd;
voidmain()
{cout.precision(4);cout<<123<<endl; //以默认方式输出123
cout<<123.456<<endl; //输出123.5
cout<<1234.56<<endl; //输出1235
cout.setf(ios::scientific,ios::floatfield); //设置scientific方式
cout<<123<<endl; //输出123
cout<<"["<<123.456<<"]\n"; //输出[+1.2346e+002]
cout.unsetf(ios::scientific); //取消scientific方式cout<<123.456<<endl; //输出123.5
cout.setf(ios::fixed,ios::floatfield); //设置fixed方式
cout<<“[”<<123<<“]\n”; //输出123
cout<<“[”<<123.456<<“]\n”; //输出123.4560
}
当precision与scientific或fixed合用时,该精度指小数点后的总位数。对于precision,若未设定scientific或fixed,则精度是不含小数点的总位数,若整数位数大于给出的精度,则按实际输出。8.3.2ios类和istream类的成员函数
ios类中提供了一些公有成员函数。因为cin是istream类的对象,所以它可调用istream类和ios类的成员函数。
1.主要函数的功能
1) istream::read
read用于无格式输入。它的函数原型:
istream&read(char*pch,intnCount);该函数的功能是从输入流中读入nCount个字符放在pch所指向的单元中,尾部不加\0。它是不能保留的,即设置后一次有效。
2) ios::width
width用于设置域宽。该函数的功能是设置允许输入流一次可读入的字符个数。它是不能保留的,即设置后一次有效。
3) istream::get
get用于输入字符。它的函数原型主要有三个:
intistream::get();从(输入流)键盘中读取一个字符,并返回该字符值,若已到达文件的末尾,则返回EOF。
istream&get(char&ch);
从输入流中读取一个字符存储在ch中,它返回被引用的istream对象。
istream&get(char*pch,intnCount,chardelim=‘\n’);。
从(输入流)键盘中读取nCount-1个字符,放入pch指向的单元中,若其中发现了'\n'或到达了文件的末尾,则提前结束。
4) istream::getline
getline用于输入字符。它的函数原型:
istream&getline(char*pch,intnCount,chardelim=‘\n’);
该函数的功能是从(输入流)键盘中读取nCount-1个字符,放入pch指向的单元中,若其中发现了‘\n’或到达了文件的末尾,则提前结束。
5) istream::gcount
gcount用于统计输入字符的个数。它的函数原型:
intgcount()const;
2.应用举例
因为cin就是istream流类对象之一,所以我们以它为例来说明istream流类成员函数的使用方法。【例8-4】演示read、gcount、width的使用。
#include<iostream>
usingnamespacestd;
constintN=80;
voidmain()
{inti=0;chara[N];
cout<<"Enterthefirstsentence:\n";
cin.read(a,9); //仅在以后的一次输入流中能读入9个字符
cout<<"Thesentenceenteredwas:"<<endl;cout.write(a,cin.gcount());
//输出a数组中的cin.gcount()个字符
cout<<endl;
cout<<"Enterthesecondsentence:"<<endl;
while(i<3) //控制读入3次
{cin.width(3); //一次读2个字符,即两字符后加\0结尾
cin>>a; //将读入的两个字符串放入a中
cout<<"a:"<<a<<'\n'; //输出
i++;
cout<<"i:"<<i<<endl;
}
}运行结果:
Enterthefirstsentence:
12345abcdefghijk↙
Thesentenceenteredwas:
12345abcd
Enterthesecondsentence:
a:ef
i:1
a:gh
i:2
a:ij
i:3程序中的cin.gcount()用来统计输入流中的字符数。由于前面有cin.read(a,9);,所以,cin.gcount()的结果为9,于是运行结果就输出a数组中的9个字符。
【例8-5】演示用get输入一系列字符串,用默认终止符终止输入。
#include<iostream>
usingnamespacestd;
constintSIZE=80;
main()
{charbuffer1[SIZE],buffer2[SIZE];
cout<<"Enterasentence:\n";
cin.get(buffer1,SIZE);
//从当前位置起,读入SIZE-1个字符给buffer1数组
cin.get();
//消除该终止符
cin.get(buffer2,SIZE); //读入\n即终止
cout<<"\nThestringinbuffer1was:"<<buffer1<<"\n";
cout<<"Thestringinbuffer2was:"<<buffer2<<endl;
return0;
}运行结果:
Enterasentence:
IlearnC++↙
program↙
Thestringinbuffer1was:IlearnC++
Thestringinbuffer2was:program从运行结果分析,在第一个get读入时,就将终止符之前的字符读入数组buffer1中。当第二个get读入时,首先遇到的是终止符\n,这样读取操作刚开始就结束了。数组buffer2中是由\0组成的空串。改进方法是加入cin.get();,以消除终止符,这样第二个get的读入才可起作用。
【例8-6】演示用get输入一系列字符串,通过自己指定终止符X来结束输入。
#include<iostream>
usingnamespacestd;
voidmain()
{charstr[128];cout<<“inputalineoftextandpressEnter”<<endl;
cin.get(str,sizeof(str),‘X’);
//读入后放在str中。指定X为终止符
cout<<“Firstline:”<<str<<endl;
cin.get(str,sizeof(str));
cout<<“Secondline:”<<str<<endl;
}
运行结果:
显示 inputalineoftextandpressEnter
输入 YouarestudentXYoulearnC++↙
输出 Firstline:Youarestudent
Secondline:XYoulearnC++;//注意,前面有一个X【例8-7】演示函数getline的使用。
#include<iostream>
usingnamespacestd;
voidmain()
{charstr[128];
cout<<“inputalineoftextandpressEnter”<<endl;
cin.getline(str,sizeof(str),‘X’);
//首次读入,在遇到X时结束操作
cout<<“Firstline:”<<str<<endl;
cin.getline(str,sizeof(str));
//再次读入,默认为遇到回车时结束操作
cout<<"Secondline:"<<str;
}运行结果:
显示 inputalineoftextandpressEnter
输入 YouarestudentXYoulearnC++↙
输出 Firstline:Youarestudent
Secondline:YoulearnC++; //注意,不再有X
比较例8-6和例8-7的运行结果可知,在例8-6中的“Secondline:”后多了一个X,这就是get和getline的区别。前者不从输入流中清除输入时指定的结束标记,后者从输入流中输入时清除指定的结束标记。8.3.3读取标准串
cin.getline或cin.get都是读取C格式串,即以\0结尾的串。在<string>文件中也有getline函数,它用于读取标准串。string类的字符串称为标准串,以\0结尾的字符串称为C格式串。
【例8-8】演示读取标准串。
#include<iostream>
usingnamespacestd;
#include<string>
voidmain(){stringa;cin>>a; //用cin读标准串,读入时遇空格等结束
cout<<a<<endl;
getline(cin,a); //从键盘中读取标准串
cout<<a<<endl;
}
运行结果:
IlearnC++↙
I //读入一个单词
learnC++
//learnC++前有空恪
在本例中直接用cin读取标准串,由于用cin输入时遇空格或回车便结束,于是cin>>a;只能读入一个单词,即I。8.4.1概念与应用
在C++中除了类成员函数之外,C++的流类库还提供了更方便的输入/输出格式化方法,该方法是使用一种称为流操作算子(或操作符)的特殊函数。流操作算子的特点是能直接包含在输入/输出表达式中,所以使用更方便。
所有流操作算子均在头文件iomanip中作了定义,使用时要包含该头文件。一些常用的流操作算子如表8-3所示。8.4流 操 作 算 子表8-3流操作算子表
【例8-9】演示用流操作算子来控制在输入/输出中的数制。数制设定后能保留。
#include<iostream>
#include<iomanip>
usingnamespacestd;
voidmain()
{intm,n;
cin>>oct>>m; //输入数据为八进制
cin>>n; //仍为八进制
cout<<"m(DEC)is"<<m<<ends; //输出十进制数
cout<<",n(DEC)is"<<n<<endl;cout<<m<<“Octalis”<<oct<<m<<endl;
//第一个m为十进制,第二个m为八进制
cout<<n<<“DECis”<<dec<<n<<endl;
//第一个n为八进制,第二个n为十进制
}
运行结果:
1215↙
m(DEC)is10,n(DEC)is13
10Octalis12
15DECis13【例8-10】演示域宽和精度的处理方法。
#include<iostream>
#include<iomanip>
#include<cmath>
usingnamespacestd;
voidmain()
{intm=20,n=12345;cout<<n<<'['<<setw(4)<<m<<']'<<'['<<m<<']'<<endl; //域宽设置仅一次有效
cout<<setw(4)<<n<<endl; //n域宽太小,按实际输出
cout<<m<<"Hexadcimalis"<<hex<<m<<endl; //设置为十六进制,并能保留
cout<<'['<<setw(8)<<n<<']'<<m<<endl; //设置域宽
cout<<m<<“Decimalis”<<dec<<m<<endl; //改为十进制方式
doubleroot=sqrt(2);
cout<<root<<endl;
//输出浮点数,默认为六位,不含小数点
cout<<setprecision(8)<<root<<endl;
//不含小数点总共八位,并可保留
cout<<root<<endl;//仍旧保留为八位,直到下一次改变为止
}运行结果:
12345[20][20] //系统默认右对齐
12345
20Hexadcimalis14
[ 3039]14
14Decimalis20
1.41421 //系统默认六位,不包含小数点
1.4142136 //共八位不包含小数点
1.41421368.4.2自编流操作算子
我们可以自己编写流操作算子,方法为编写一个带有ostream&参数的函数,并返回该参数。若我们希望使用新的流操作算子HEX,则实现如下:
#include<iostream>
usingnamespacestd;
ostream&HEX(ostream&s)
{s.setf(ios::hex,ios::basefield);
returns;
}
voidmain()
{cout<<HEX<<315<<endl;} //输出13b8.4.3流操作算子与状态标记合用
并不是说有了流操作算子状态标记就可取消,对于有些情况,需要流操作算子与状态标记合用。我们可以在使用流操作算子时提供状态标记作为参数。
【例8-11】通过状态标记和流操作算子两者合用,输出浮点数。
#include<iostream>
#include<iomanip>
usingnamespacestd;
voidmain()
{doublenum=22.0/7;cout<<num<<endl; //默认输出3.14286,总共六位
cout<<setiosflags(ios::hex)<<setprecision(4)<<num<<endl;
cout<<1.90<<endl; //精度还是4,不输出无效的0
cout<<setiosflags(ios::showpoint)<<1.90<<endl;
//强制输出小数点后无效的0
cout<<setiosflags(ios::fixed)<<setprecision(4)<<num<<endl;
cout<<1.90<<endl;
//小数仍保留4位cout<<resetiosflags(ios::fixed); //取消浮点显示
cout<<setiosflags(ios::scientific)<<num<<endl;
//用指数形式输出
cout<<resetiosflags(ios::scientific)<<num<<endl;
//取消指数法显示
cout<<setiosflags(ios::fixed)<<num<<endl;
//改用浮点数输出
cout<<setiosflags(ios::showpos)<<num<<endl;
//在正数前输出+号,输出+3.1429
}运行结果:
3.14286 //精度默认为6
3.143 //hex不起作用
1.9 //不输出无效的0
1.900 //精度为4
3.1429 //小数后有4位数1.9000 //小数后仍旧保留4位数
3.1429e+000
3.143 //取消指数形式后的显示。此时的精度是4
3.1429 //又设定浮点数,所以4是小数后的位数
+3.1429 //正数前强制输出+号8.5.1概念
ofstream、ifstream和fstream是文件流类,分别是输入、输出和输入/输出文件流,它们在fstream中定义。使用这三个类时必须用#include命令将fstream头文件包含进来。
文件流类不是标准设备,所以没有像cout那样预先定义为全局对象。文件流类定义的操作应用于外部设备,最典型的是磁盘文件。若要定义一个文件流类对象,就会自动调用该类的构造函数。8.5文件流类定义文件流类对象的方法:
类名对象名(文件名,打开方式和保护方式);
其中,文件的打开方式和保护方式参见表8-4。表8-4文件使用模式在文件流类的处理中还有几个重载构造函数,其中常用的是下面几种。
(1)用于创建ofstream类对象的构造函数:
ofstream::ofstream();
ofstream::ofstream(constchar*,int=ios::out,
int=filebuf::openprot); //打开指定文件
(2)用于创建ifstream类对象的构造函数:
ifstream::ifstream();
ifstream::ifstream(char*,int=ios::in,int=filebuf::openprot);其中,第1个参数是文件名,ios::in是打开方式,filebuf::openprot是保护方式。
(3)用于创建fstream类对象的构造函数:
fstream();
fstream(constchar*,int=ios::in|ios::out,int=filebuf::openprot);
其中,第1个参数是文件名,第2个参数是打开方式,第3个参数是文件保护方式。第2、3个参数都可以取默认值。8.5.2使用方法
1.通过构造函数打开文件
ofstream和fstream都具有重载的构造函数,在定义流对象时也可以给出0~3个指定参数。例如:
ofstreamout_file(“filename.txt”);
在定义文件流对象时,通过构造函数打开文件,于是就在文件流对象与文件之间建立了联系。
2.通过成员函数打开文件
可以在定义了流对象之后,使用流类对象的成员函数open()打开文件,用close()关闭文件。打开文件之后就在文件流对象和文件之间建立了一个连接关系,关闭文件之后就断绝了文件流对象和文件之间的连接关系。
打开文件函数open()的原型有几种表现形式。在ifstream类中为:
voidopen(constchar*,int=ios::in,int=filebuf::openprot);
在ofstream类中为:
voidopen(constchar*,int=ios::out|ios::trunc,int=filebuf::openprot);
在fstream类中为:
voidopen(constchar*,int=ios::in|ios::out,int=filebuf::openprot);以上三个open函数,都至少提供一个参数文件名。
一般情况下,都是用构造函数打开文件。ifstream、ofstream和fstream都有自己的构造函数,它们具有与open函数相同的参数和默认值。用构造函数打开文件的同时,流对象和文件之间就建立了联系。
为了能对文件进行操作,必须先定义文件流类的对象。例如:
ofstream out_file; out_file.open("filename.txt");先定义了一个输出流对象,自动调用无参构造函数,然后通过out_file.open(“filename.txt”);就建立了文件流类对象和文件的联系,在此我们称为打开文件。同理:
ifstreamin_file.open(“filename.txt”);
//定义了一个输入流对象,并指定输入文件
fstreamio_file.open(“filename.txt”);
//定义了一个输入/输出流、文件流对象,并指定操作文件8.5.3应用举例
通过文件流类的使用可以把一个文件连接到程序上,以便输入和输出。使用文件流类,首先要包含fstream头文件。
1.ifstream和ofstream的应用
【例8-12】演示简单的文件读/写操作。
#include<fstream>
#include<iostream>
usingnamespacestd;
voidWrite()
{doublex;ofstreamoutfile(“score.txt”);
//打开或新建名为score.txt的文件
outfile<<"数学"<<'\t'<<"C++"<<endl; //写入表头
cin>>x;outfile<<x<<'\t'; //写入成绩
cin>>x;outfile<<x<<'\t';
}
voidRead(){doublex;chars[20];
ifstreamInfile(“score.txt”);
//打开或新建名为score.txt的文件
Infile>>s;cout<<s<<‘\t’; //读入表头,输出时加入跳格
Infile>>s;cout<<s<<‘\t’<<endl;
//读入表头,输出时加入跳格和回车
Infile>>x;cout<<x<<‘\t’; //读入成绩,输出时加入跳格
Infile>>x;cout<<x<<‘\t’<<endl;
//读入成绩,输出时加入跳格和回车
}
voidmain()
{Write()
Read();
}
【例8-13】在当前目录下建立一个名为sea的文件,内容为:
Whatdoyouknowaboutthesea?Weknowthatitlooksveryprettywhenthesunisshinningonit.Wealsoknowthatitcanbeveryroughwhenthereisastrongwind.Whatotherthingsdoweknowaboutit?
建立sea文件的代码为:
#include<iostream>
#include<fstream>
usingnamespacestd;
voidInput()
{charch;cout<<"Inputtexty/n?"<<endl;
cin>>ch;
if(ch=='n'||ch=='N')return;
cout<<"Inputtext.Endupas#"<<endl;
ofstreamoutfile("sea.txt"); //打开或新建名为sea.txt的文件
cin.get(ch); //先输入的第一个字符
while(ch!='#') //输入以上内容并以#结束
{outfile.put(ch);cin.get(ch);}//存入文件sea中
outfile.close(); //关闭文件
}接上例,若在当前目录中有名为sea.txt的文件,则下面的程序用于打开该文件,并将它输出显示。
voidOutput()
{charfile[20],ch;cout<<“Pleaseinputfilename”<<endl;
cin>>file;
//输入要打开的文件名和后缀
ifstreaminfile(file); //打开文件
if(!infile) //判断打开是否有效
{cout<<“Sorryunabletoopen”<<file<<endl;exit(-1);}
while(infile.get(ch)) //从打开的文件中读入字符赋给ch
cout.put(ch);
//将ch中的字符输出到显示器
cout<<endl;
infile.close();
}接上例,若在当前目录中有名为sea.txt的文件,则下面的程序用open函数打开该文件,将其改名后另存到c盘根目录下,并且使文件为未知类型。
voidSave_as()
{charfile[20];cout<<“pleaseinputfilename”<<endl;
cin>>file;
//输入一个要打开的文件名
ifstreamopenfile;
openfile.open(file); //打开文件
ofstreamnewfile("c:\\dahai");//在c盘建立一个dahai的文件
if(!openfile)
{cout<<"Sorryunabletoopen"<<file<<endl;exit(-1);}charch;
while(openfile.get(ch))
//从被打开的文件中读字符存于ch中
newfile.put(ch);
//将ch中的字符存于新建立的文件中
openfile.close();
newfile.close();
}
voidmain()
{Input();
Output();
Save_as();
}
2.fstream的应用
用ifstream或ofstream可以建立输入或输出文件,用fstream既可以建立输入文件,又可以建立输出文件。
【例8-14】演示fstream的使用和文件的输入与输出。
#include<iostream>
#include<fstream>
usingnamespacestd;
voidRebuild() //输入
{charch;cout<<“Inputtexty/n?”<<endl;
cin>>ch;
if(ch==‘n’||ch==‘N’)return;
fstreamiofile(“sea.txt”);
//以读/写方式打开名为sea的文本文件
while(ch!=‘#’) //约定输入字符以#结束
{cin.get(ch);iofile.put(ch);}
//从键盘中读入后,存入文件sea中
iofile.close();
}voidOutfile() //输出
{charch;
fstreamoutfile("sea.txt");
//以读/写方式打开名为sea的文本文件
while(outfile.get(ch)) //先将字符读入到ch中
cout.put(ch); //再送给显示器
outfile.close();
}
voidmain()
{Rebuild();
Outfile();
}
fstream与ofstream的区别是:虽然用fstream或ofstream以默认方式打开的文件都具有写入功能,但用ofstream打开时若该文件不存在,则它具有新建一个文件的能力,而fstream却没有这个能力。在本例中sea.txt是文本文件,即Windows窗口中记事本文件,若用sea.doc就是Word文件。在C++中提供了一些处理字符的类——串流类,它们的继承关系可以由图8-2来描述。
ostringstream、istringstream、stringstream是串流类,在sstream头文件中定义,使用时要包含相关头文件。8.6串流类图8-2串流类的继承关系8.6.1串流类处理字符串
在C++中引入sstream的主要作用是可以使用字符串类,在sstream头文件中已包含了string头文件。
【例8-15】演示串流类istringstream的使用。
#include<iostream>
#include<sstream>
usingnamespacestd;
voidmain(){charstr[100]="Youareastudent.\n";
chara[10],b;
istringstreamai(str); //把str看做提取设备
ai>>a; //从ai中提取一串字符
cout<<a<<endl; //插入一个字符
ai>>b; //从ai中提取一个字符
cout<<b<<endl;
}
【例8-16】演示串流类istringstream结合string类的使用。
#include<iostream>
#include<sstream>
usingnamespacestd;
voidmain()
{stringstr="Youareastudent.\n"; //定义串流类对象
chara[10],b;
istringstreamai(str);
ai>>a;
cout<<a<<endl;
ai>>b;
cout<<b<<endl;
}
【例8-17】演示串流类ostringstream结合string类的使用。
#include<iostream>
#include<sstream>
usingnamespacestd;
stringparseString(char*pString)
{istringstreaminp(pString);
intNumber;floatbalance; //帐号和余额
inp>>Number>>balance; //自动以空格等分隔ostringstreamoutp;
outp<<"aNumber="<<Number<<",balance="<<balance<<'\0';
returnoutp.str(); //将ostringstream对象转换为string对象
}
voidmain()
{char*str="1234100.35";
stringpBuf=parseString(str); //定义串流类对象
cout<<pBuf<<endl;
}
该例中串流类的成员函数str()返回与ostringstream类对象相关连的string类对象。在ostringstreamoutp中并没有指出outp的大小,它由提取字符的长度决定。8.6.2串流类处理文件
【例8-18】若在当前目录中已经有文件sea,我们在Input函数中将它打开,并将文件内容读入buf中,然后在主函数中将它输出。
#include<iostream>
#include<fstream>
#include<sstream>
usingnamespacestd;stringInput()
{charch,name[40];
cin>>name;
fstreamfile_in(name); //打开文件备读
ostringstreambuf;
while(file_in.get(ch))
buf.put(ch);returnbuf.str(); //将buf转换成string对象再返回
}
voidmain()
{stringtext=Input();
cout<<text<<endl;
}
要了解更多的操作,请查看类库手册。8.7.1异常处理的概念
程序可能按我们的意愿终止,如用return语句或exit函数,也可能因为程序中的某种不当而异常终止。异常是由系统运行造成的可以预料、但不可避免的事件。例如,内存空间不足、在硬盘上的文件被挪离、软盘没有放好、在运算时除数为0或数组下标越界等,这些都是运行中的异常。异常处理(exceptionhandling)就是提供对可以预料的情况进行必要处理的方法。它是处理程序运行期间错误的一种结构化方法。8.7异常处理8.7.2异常处理的方法
在C++中异常处理由抛出异常、捕获异常和处理异常三部分组成。抛出异常即根据设置条件决定是否抛出异常;捕获异常就是寻找最佳匹配;处理异常即完成善后工作。C++通过三个保留关键字try、throw和catch来实现异常处理。
1.语法结构
实现异常处理的基本结构由try块和catch块组成。其中try块的作用是检测和抛出异常,catch块的作用是捕获和处理异常。
1) try块
关键字try和它后面由“{”、“}”所包围的语句块组成try块。在该语句块中包含用户要求监测的异常语句和抛出异常语句。抛出异常可通过throw表达式来实现,它由关键字throw后跟一个表达式构成,该表达式的类型是被抛出异常的类型。
2) catch块
在try块之后紧接着是catch与“{”、“}”所包围的语句,我们称为catch子句。这些catch子句组成的子句集合称为catch块或称异常处理器。该语句块的主要任务是捕获异常,并通过catch子句来完成处理工作。程序员就在该语句块中添加一些语句来实现善后处理工作。例如:
try{ //try块开始
… //正常要求完成的功能语句
throwexception //发生异常时抛出exception
… //正常要求完成的功能语句
} //try块结束catch(参数类型1){…异常发生后的处理语句块…} //catch子句
catch(参数类型2){…异常发生后的处理语句块…} //catch子句
catch(参数类型n){…异常发生后的处理语句块…} //catch子句
以上各catch子句的集合称为catch块。要注意C++语法规定,try块和catch块之间不能有其他语句。
2.执行过程
在C++中将由try和catch块组成的结构称为异常机制,它使用的是将错误报告和错误处理相分离的方法。其中,try和监测异常的语句块称为错误报告;catch和处理语句块称为错误处理。
C++的异常处理机制被称为是不可恢复的,也就是说,一旦抛出异常,程序的执行就不能够在异常被抛出的地方继续。它的执行步骤可以简单地描述如下:
(1)检测。检测结束后,如果不发生异常,则只执行检测和其他正常功能的语句。
(2)抛出。如果在try块内处理某程序时发生异常,则通过throw抛出一个exception信息,并以抛出异常的语句作为出口,在抛出异常的同时跳出try块。在该try块中,从抛出点开始到try块结束之间的语句不再执行。
(3)捕获与处理。被抛出的异常就在各catch子句声明的参数中寻找一种严格匹配,当寻找到某一catch匹配时,就执行该catch子句。除了被匹配的catch子句外,其他的catch子句就不再执行了。然后程序执行catch块后面的语句。
【例8-19】引入异常机制,实现两数相除。
#include<iostream>
usingnamespacestd;
voidDivide(doublex,doubley)
{doublez;
try
{if(y==0) //检测异常
throwy;
//抛出异常
z=x/y;
cout<<z;
}
catch(double){cout<<"exceptofdividingzero"<<endl;}
catch(...){}
}
voidmain()
{doublex,y;cin>>x>>y;
Divide(x,y);
}该例的执行过程是:若y为0,则在try块中抛出y,z=x/y;将不再执行,继而在catch块中找到匹配后输出提示信息,并结束函数Divide的调用;若y不为0,则输出x/y的值。
在try后的throw可以是任何类型的数据。当发生异常由throw抛出一个数据时,根据该数据的类型,在catch的括号中寻找一个严格的匹配,找到后就执行该子句,否则执行默认的catch子句。从throw抛出口开始,到try块结束的语句不再执行。
3.异常处理的规则
(1) try和catch必须成对出现。try至少要有一个catch语句与之对应。任意数量的catch块只能紧跟在try块之后,即catch块和try块之间不能有其他语句。
(2)可以有单独的throw。有单独的throw而不提供默认的catch子句,不是好方法。当有抛出而没有被匹配时,就调用unexpected()函数,unexpected()又调用terminate()函数,terminate()又调用abort()函数,使程序紧急终止,这是一种不受欢迎的终止方式。
(3)引发异常的throw应该放在try块内,或者放在由try块中直接或间接调用的函数体内。
(4) catch的参数表中最多只能有一个参数。catch的参数表中可以不给或只给出一个参数。若在catch语句块中要使用被抛出的对象,那么在catch子句的参数表中就要给出对象名。这样一来,当抛出异常后就要发生拷贝,为了减少时间开销,可以用指针或引用作为参数。
(5)建议提供一个默认的catch子句。默认的catch子句的参数用(…)表示,并将它放在catch子句的最后。catch(...)称为默认异常,它能捕获任何异常。不要将此默认异常放在其他catch语句块之前,因为它对任何throw的抛出都能接收。
4.实现异常处理的步骤
首先定义异常try语句块,将所有可能产生错误的语句框定在try块内。然后完成抛出异常throw语句,判断是否发生异常,若是则抛出异常信息。最后定义异常处理的catch语句块,将异常处理的语句放在catch块内,完成异常处理。8.7.3异常处理的结构与要求
1.异常处理的结构
异常处理按结构来分,有两种处理方法:方法一,异常派生层次结构;方法二,异常枚举族系。下面通过两个例子来介绍。
【例8-20】演示抛出多种类型的数据及捕捉的方法,说明异常派生层次结构。
#include<iostream>
#include<cmath>usingnamespacestd;
classCode
{public:
Code(inta,floatb){u=a;v=b;}
private:
intu;floatv;
};
voidTest(intx)
{inty=8,a[10];doublez;
try{ if(x==0)throwx;elsey=y/x; //抛出int
if(x>10&&x<50)throw‘A’;elsea[0]=y; //抛出char
if(x<0)throw3.1;elsez=sqrt(x); //抛出double
if(x==8)throwCode(3,3.4f); //抛出class
if(x>50)throw“program”; //抛出char*
cout<<“normal”<<endl;
//若不发生异常能执行到该句
}catch(int){cout<<“exceptofdividingzero”<<endl;}
catch(char){cout<<“erroroverbounder”<<endl;}
catch(double){cout<<“errorx<0”<<endl;}
//声明时只给出类型
catch(char*p){cout<<p<<endl;}
//语句块中使用p,参数中就要给出
catch(Code){cout<<“throwCode”<<endl;}
catch(...){cout<<“default...”<<endl;}
//默认必需(...),即三小点不可少
cout<<"Testprogramreturn"<<endl;//总要执行该句
return;
}voidmain()
{intx;cout<<"inputanumber"<<endl;cin>>x;
Test(x);
cout<<"mainend"<<endl; //总要执行该句
}当throw抛出某类型的数据或对象之后,就在catch块中按从上到下的顺序,根据类型来寻找匹配。其中的匹配都是严格匹配,不存在转换匹配。在catch块中,只执行匹配的catch子句,其余的catch子句不执行。但catch语句块之后的其他语句仍要执行,如上例中的cout<<"Testprogramreturn"<<endl;。
【例8-21】演示用枚举族系进行异常处理。
#include<iostream>
usingnamespacestd;
enumException{noPrep,wrongFormat,noExit,zero};
i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 药渣委托处理协议书
- 酒店事故赔偿协议书
- 路政改革扩建协议书
- 酒店经营管理协议书
- 邻居违建协商协议书
- 门面转让退款协议书
- 项目资料移交协议书
- 解除店长职位协议书
- 邻居物资互换协议书
- 隐名股东股东协议书
- 部编版八年级下册历史期末100道选择题专练
- 改进工作作风自查报告(11篇)
- 典型任务-无线调车灯显设备使用讲解
- 24春国家开放大学《机械CADCAM》形考任务1-3参考答案
- 山东省烟台市牟平区(五四制)2023-2024学年九年级下学期期中考试数学试题
- 施工升降机安装验收表
- 2024年咸阳职业技术学院单招职业技能测试题库及答案解析
- RBA-6.0-培训教材课件
- 《客舱安全与应急处置》-课件:灭火设备:机载灭火瓶
- 2020年10月自考00445中外教育管理史试题及答案含解析
- 国际关系史智慧树知到期末考试答案2024年
评论
0/150
提交评论