第十章IO流.ppt
第十章 I/O流,10.1 C+流库概述,所谓“流”,就是数据从源(数据的生产者)到漏(数据的消费者)的流动。可以把一个流简化地理解为,提供字符的源(称为输入流)或收集字符的漏(称为输出流)。C+流库是C+语言为完成输入/输出工作而预定义的类的集合,这些类构成一个层次结构的系统。streambuf类主要负责缓冲区的处理。图10.1为它们之间的层次关系。,10.1 C+流库概述,图10.1strstreambuf类及其派生类,所谓“流”,就是数据从源(数据的生产者)到漏(数据的消费者)的流动。,10.1 C+流库概述,filebuf类扩充了streambuf类的功能,它能够处理文件。把filebuf同某个文件的描述字相联系就称为打开这个文件。conbuf类也扩充了streambuf类的功能,它能处理输出,为输出操作提供缓冲区管理。ios类及其派生类为用户提供了使用流类的接口。输出流(ostream)针对系统全部预定义类型重载了输出运算符“”,它提供了流的主要输出操作;文件流(fstreambase)提供了文件流的公共操作;串流(strstreambase)专门处理串流。请参考教材图10.2 ios类及其派生类。,图10.2 ios类及其派生类,10.1 C+流库概述,当开始执行C+程序时,系统将自动打开几个预定义流,用户可以在程序中直接使用它们,它们是:cin,称为标准输入流,缺省时为键。如:int m,n;cinmn;/100 22输入时数据间加以空格 cout,称为标准输出流,缺省时为显示器。如:int x=9;cout“x=“xendl;/x=9 cerr和clog都是ostream_withassign类的对象,称为标准错误输出流,固定关联到 显示器。,#include#include using namespace std;void main()string in_string;/向用户终端写字符串cout in_string;if(in_string.empty()/产生一个错误消息输出到用户终端cerr error:input string is empty!n;else cout hello,in_string!n;,10.2 预定义类型的输入/输出,istream流类相对于系统预定义类型把运算符“”重载为输入运算符;ostream流类相对于系统预定义类型把运算符“”重载为输出运算符。,10.2.1 istream流类,istream类中有如下的主要成员函数:get函数从流中把字符输入到给定的char*,直到遇到分界符、文件结束符或已读完(len-1)个字符为止:istream,getline函数 由于程序员常常忘了在应用get()之前丢弃 换行符n,所以使用成员函数getline()要比get()更好,因为它丢弃换行符n,而不是将其留作istream 的下一个字符。getline()的语法与get()的三参数形式相同,(它也返回被调用的istream 对象):istream 它们的功能是,从流中提取给定数目的字符到数组char*中。出错时可以用函数gcount得到实际读入的字符数。下面演示一个使用get,getline 和read读出文件内容的例子。,10.2.1 istream流类,#include#include using namespace std;void main()char ch,c030,c130,*p=abcde fghij klmno pqrst uvwsyz;ofstream tfile(aaa.txt);tfile.write(p,strlen(p)+1);/写入文件tfile.close();ifstream in0(aaa.txt);in0c0;/读到第一个空格结束coutc0;/输出到屏幕coutendl;while(in0.good()/从第一个空格开始继续读文件in0.get(ch);/使用get从文件读入if(!ch)break;coutch;/使用cout输出in0.close();coutendl;ifstream in1(aaa.txt);in1.getline(c1,30,r);/使用getline从文件头读到r并舍弃rin1.read(c0,30);/从r之后继续读文件coutc1endlc0endl;,程序输出结果:abcde fghij klmno pqrst uvwsyzabcde fghij klmno pqst uvwsyz,10.2.2 ostream流类,ostream类中有如下主要成员函数:flush函数ostream 它们的功能是向流中输出几个字符,第一个参数指向待输出的字符串。,演示使用write函数和put函数和例子。#include#include using namespace std;void main()char*p=Where are you?;ofstream myfile(aa.txt);myfile.write(p,5);/写入文件cout.write(p,5);/写到屏幕for(char ch=a;ch=z;ch+)myfile.put(ch);/写入文件cout.put(ch);/写到屏幕myfile.put(0);/写入文件结束符myfile.close();,可以直接打开查看文件aa.txt的内容,它与屏幕上显示的内容一样,均为:Whereabcdefghijklmnopqrstuvwxyz,write函数可以将内存数据写入文件。它的第1个参数是char型指针,第2个参数是所要写的字节数。当遇到空字符时并不停止,而且当它遇到代码10时,还会自动扩充为回车换行符。这个函数可以二进制方式写入数据,这时的第1参数需要用char*作强制转换。#include#include using namespace std;void main()int a5=1,2,3,4,5;char*p=Where are you?;ofstream tfile1(aaa.txt);/建立文本文件tfile1.write(p,strlen(p);/写入文本文件ofstream tfile2(aaa.dat,ios:binary);/建立二进制文件tfile2.write(char*)a,sizeof a);/数组名即首地址,char*强制转换tfile1.close();tfile2.close();,输入主要由右移操作符 来支持,例如在下面的程序中从标准输入读入一个int型的值序列并把它放在一个vector 中#include#include using namespace std;void main()vector ivec;int ival;while(cin ival)ivec.push_back(ival);/.子表达式 cin ival从标准输入读入一个整数值,如果成功,则把该值拷贝到ival 中.这个子表达式的结果是左边的istream 对象在这种情况下,即cin 自己.,10.2.3 输入运算符,表达式 while(cin ival)从标准输入读入一个序列,直到cin 为false 为止.有两种情况会使一个istream 对象被计算为false:1、读到文件结束(在这种情况下,我们已经正确地读完文件中所有的值)2、遇到一个无效的值,比如3.14159(小数点是非法的)、1e-1 字符(文字e 是非法的)或者一般的任意字符串文字。在读入一个无效值的情况下,istream 对象被放置到一种错误的状态中,并且对于值的所有读入动作都将停止。,10.2.3 输入运算符,10.2.3 输入运算符,运算符输入预定义类型数据时的注意事项:在缺省情况下,运算符跳过空白符,然后读入与输入变量类型相对应的值。当输入字符串(即,类型为char*的变量)时,运算符“”的作用是跳过空白,读 入以下的非空白字符,直到遇到另一个空白字符为止,并在串尾放一个字符“0”。不同类型的变量一起输入时,系统除了检查是否含有空白符之外,还完成输入数据与变量类型的匹配。输入运算符“”采用左结合方式工作,并返回它的左操作数,因此,可以把多个输入操作组合到一个语句中。,最常用的输出方法是在cout 上应用左移操作符(using namespace std;void main()cout gossipaceous Anna Livian;在用户终端上将输出以下内容:gossipaceous Anna Livia 输出操作符可以接受任何内置数据类型的实参,包括const char*,以及标准库string 和complex 类类型。任何表达式包括函数调用,都可以是输出操作符的实参,只要它的计算结果是一个能被输出操作符实例接受的数据类型即可。例如:,10.2.4 输出运算符,#include#include using namespace std;void main()cout The length of ulysses is:t;cout strlen(ulysses);cout n;cout The size of ulysses is:t;cout sizeof(ulysses);cout endl;在用户终端上输出如下内容:The length of ulysses is:7The size of ulysses is:8endl 是一个ostream 操纵符,它把一个换行符插入到输出流中,然后再刷新ostream 缓冲区.,10.2.4 输出运算符,下面的程序展示了一种令人迷惑的现象,我们的目的是输出pstr 所包含的地址值:#include using namespace std;const char*str=vermeer;void main()const char*pstr=str;cout(const_cast(pstr)编译并运行程序得到了我们所期望的输出:The address of pstr is:0 x116e8,10.2.4 输出运算符,下面是另一个令人迷惑的现象,我们的目的是显示两个值中的较大值:#include using namespace std;inline voidmax_out(int val1,int val2)cout val2)?val1:val2;void main()int ix=10,jx=20;cout The larger of ix;cout,jx is;max_out(ix,jx);cout endl;但是在编译并运行程序后却生成了如下不正确的结果:The larger of 10,20 is 0,10.2.4 输出运算符,问题在于输出操作符的优先级高于条件操作符,所以输出val1 和val2 比较结果的true/false 值.即表达式cout val2)?val1:val2;被计算为(cout val2)?val1:val2;因为val1 不大于val2,所以计算结果为false,它被输出为0.为了改变预定义的操作符优先顺序,整个条件操作符表达式必须被放在括号中cout val2?val1:val2);这次产生了正确的输出The larger of 10,20 is 20,10.2.4 输出运算符,10.2.4 输出运算符,使用输出运算符输出预定义类型数据时的注意事项。输出运算符“”也采用左结合方式工作,并且返回它的左操作数,因此,可以把多个输出操作组合到一个语句中,使用起来很方便。使用输出运算符“”进行输出操作时,不同类型的数据也可以组合在一条语句中,例如上面的例子中就是把整型变量和双精度型变量的输出放在同一条语句中。重载并不能改变运算符的优先级,因此,必须特别注意表达式的求值顺序。,10.3 格式控制,C+语言提供了两种格式控制方法:一种方法是使用ios类中有关格式控制的成员函数;另一种方法是使用称为操纵符的特殊类型的函数。,10.3.1 用ios类成员函数控制格式,用成员函数操作状态标志设置状态标志。ios中定义了用于设置状态标志的成员函数setf,它的原型为:long ios:setf(long flags);清除状态标志。取状态标志。下面举一个综合使用上述三种操作状态标志的成员函数的例子:#include using namespace std;void showflags(long f)long i;for(i=0 x8000;i;i=i 1)/使i中为1的位不断右移if(i,10.3.1 用ios类成员函数控制格式,void main()long f;f=cout.flags();/取当前状态标志 showflags(f);cout.setf(ios:showpos|ios:scientific);/追加标态字f=cout.flags();showflags(f);cout.unsetf(ios:scientific);/去掉scientifin标志 f=cout.flags();showflags(f);,程序输出如下:001000000000000100101100000000010010010000000001,10.3.1 用ios类成员函数控制格式,设置域宽、填充字符和精度的成员函数设置域宽。int ios:width()返回当前的域宽值。int ios:width(int wid)此函数用于设置域宽,并返回原来的域宽值。注意,所设置的域宽仅对下一个流输出操作有效,当一次输出操作完成之后,域宽又恢复为0。设置填充字符。设置显示精度。,下面举一个例子,说明上述各种成员函数的使用方法:#include using namespace std;void main()cout default width is cout.width()n;cout default fill is cout.fill()n;cout default precision is cout.precision()n;cout 666 123.45678 n;cout.precision(3);cout.width(8);cout current width is cout.width()n;cout current precision is cout.precision()n;cout 666 123.45678 456.78 n;cout current width is cout.width()n;cout.fill(*);cout.width(8);cout 666 123.45678 n;,10.3.1 用ios类成员函数控制格式,该程序的输出结果为:default width is 0default fill isdefault precision is 0666 123.45678current width is 8current precision is 3666 123.457 456.78current width is 0*666 123.457,10.3.1 用ios类成员函数控制格式,从上列输出结果可以看出:缺省域宽为0,即用密集状态输出;缺省的填充字符为空格;缺省的输出精度为0,即按照数据的实际精度输出;设置了显示精度之后,若数据的实际精度与设置的精度不一致,则输出方法如下:实际精度大于设置的精度时,四舍五入后按照设置的精度输出;实际精度小于设置的精度时,按照实际精度输出;设置域宽之后,只对其后最接近它的第一个输出有影响,第一个输出完成后系统立即把域宽置为0。,10.3.1 用ios类成员函数控制格式,10.3.2 用控制符控制格式,标准控制符C+提供了下列标准的控制符:dec:设置十进制转换基格式标志,用于输入/输出。hex:设置十六进制转换基格式标志,用于输入/输出。oct:设置八进制转换基格式标志,用于输入/输出。ws:提取空白字符,仅用于输入。endl:插入换行符并刷新流,仅用于输出。ends:在串后插入终止空字符,仅用于输出。flush:刷新输出流,仅用于输出。,10.3.2 用控制符控制格式,setbase(int n)设置转换基格式为n(取值0,8,10,或16),缺省时为0表示采用十进制,仅用于输出。resetiosflags(long f)清除由参数f指定的格式位用于输入输出。setiosflags(long f)用参数f设置格式位,用于输入/输出setfill(int c)设置填充字符,用于输入/输出。setprecision(int n)设置浮点数精度为n,用于输入/输出setw(int n)设置域宽为n,用于输入/输出。,10.3.2 用控制符控制格式,#include#include using namespace std;void main()cout123setw(5)45688n;cout123setw(5)setfill(*)456setw(5)88n;,输出结果为:123 45688123*4568*88,10.3.2 用控制符控制格式,用户自定义控制符 C+除了提供标准的控制符和控制函数之外,也提供了用户自己建立控制符函数的方法。主要有两个:第一,当要对预先未定义的设备进行操作时,定义自己的控制函数能使得对这类设备的操作变得方便;第二,当多次重复使用几个相同的控制符时,可以把这些控制符合并在一个控制函数中,以便于用户使用。,10.3.2 用控制符控制格式,下面用一个例子说明控制函数的定义和使用:#include#include using namespace std;ostream,输出结果为:first line:25*second line:148*,10.4 自定义类型的输入/输出,C+语言创建自己的流库的主要目的,就是使用户自定义类型数据的输入/输出也能像系统预定义类型的输入/输出一样简单、方便,这通过重载输入运算符“”和输出运算符“”来实现。,10.4.1 重载输入运算符,重载输入/输出运算符时必须使用友元函数。重载输入运算符的友元函数的定义格式如下:istream,10.4.1重载输入运算符,下面举一个重载输入运算符的例子:#include#include using namespace std;class ThreeDint x,y,z;public:ThreeD(int i,int j,int k)x=i;y=j;z=k;friend istream,10.4.1重载输入运算符,istream,10.4.1重载输入运算符,几点注意事项:该重载函数的返回类型是istream类对象的引用,返回引用的目的在于,把几个输入运算符“”放在同一条输入语句中时,该重载函数仍能正确工作。重载运算符函数operator“”的第二个参数必须是一个引用。该运算符函数有两个参数,第一个参数是对istream类对象的引用,它出现在运算符“”的左边,第二个参数是出现在运算符右边的自定义类型对象。,10.4.2重载输出运算符,用友元函数重载输出运算符”“来实现用户自定义类型对象的输出。定义运算符函数的格式如下:ostream,10.4.2重载输出运算符,下面举一个例子说明重载输出运算符的方法:#include#include using namespace std;class ThreeDint x,y,z;public:ThreeD(int i,int j,int k)x=i;y=j;z=k;friend ostream,10.4.2重载输出运算符,ostream,输出结果为:11,22,3345,56,67,10.5文件的输入/输出,C+把文件当作是字符序列。按照数据的组织形式,可以把文件分成ASCII文件和二进制文件两种。ASCII文件也叫文本文件,其每个字节放一个ASCII代码,表示一个字符。所谓二进制文件,就是把内存中的数据按其在内存中的存储形式原样写到外存储器中。在C+中,为了进行文件的输入/输出,必须首先键立一个文件流,然后把这个流和实际的文件相关联,这称为打开文件。,10.5.1面向文件的流类,为处理文件的输入/输出,C+分别从istream类、ostream类和iostream类公有派生出了ifstream类、ofstream类和fstream类等三个面向文件的流类。在执行文件输入/输出之前,需要做三件事情:在程序中包含头文件fstream.h;建立文件流,即说明面向文件流类的象;打开文件,即是使某个文件和某一文件流相关联。,演示文件流的概念,#include#include using namespace std;void main()int i;char ch16,*p=abcdefg;ofstream myfile;/建立输出流myfilemyfile.open(“mytext.txt”);/建立输出流myfile和文件mytext.txt之间的关联myfilechi;/将每次读入的1个字符赋给数组的元素chichi=0;/设置结束标志gettext.close();/关闭文件mytext.txtcoutchendl;/使用cout 流向屏幕,10.5.1面向文件的流类,对文件的操作总结如下:1、打开一个相应的文件流。例如打开一个输出文件流mystream:ofstream mystream2、把这个流和相应的文件关联起来。例如语句:mystream.open(“mytext.txt”)由于ifstream、ofstream和fstream这3个类都具有自动打开文件的构造函数。因此用一条语句ofstream mystream(“mytext.txt”);就可以完成上述两步。如果指定文件路径,路径中的”号必须使用转义字符表示。例如:ifstream gettext(“f:textfilemytext.txt”);3、操作文件流。要想对文件进行操作,对相应的文件流进行操作即可。例如:可以像cout那样,使用put或write对输出流进行操作。,10.5.2文件的读写,文本文件与二进制文件的读写方法有所不同,下面分别介绍。文本文件的读写 文本文件的输入/输出,通常只需使用输入运算符“”和输出运算符“”。二进制文件的读写 读写二进制文件通常使用get(char&)和put(char)这两个函数。另一种读写二进制文件的方法是使用10.2节介绍过的read和write函数。,1.文本文件的读写:为了完成文本文件的输入/输出,通常只需使用输入运算符“”和输出运算符“#include using namespace std;void main()ofstream out(data);if(!out)cout can not open file data.;exit(1);out 256 198.69 C+Languagen;out.close();,10.5.2文件的读写,特殊格式文件的读写,10.5.2文件的读写,图 10.3 一种特殊格式文件的结构示意图,写入文本文件并读出,写入二进制文件并使用定位读出的例子。#include#include using namespace std;void main()int a5=1,3,5,7,9;int b5=2,4,6,8,10;int i;char*p,ch;p=We are here!;ofstream tfile(data.txt);/写入文本文件tfile.write(p,strlen(p);ofstream tfile2;tfile2.open(date.dta,ios:binary);tfile2.write(char*)a,sizeof a);/把a在内存区的内容写入文件tfile2.write(char*)b,sizeof b);/写btfile2.close();tfile.close();/关闭全部文件(续下页),文件读写综合实例,ifstream tfile3(data.txt);/读文本文件data.txtwhile(tfile3.get(ch)coutch;tfile3.close();coutnb:;ifstream in(date.dta,ios:binary);/文件data.txt与in关联in.read(char*)b,20);/将a原来的内容读入bin.read(char*)a,20);/将b原来的内容读入afor(i=0;i5;i+)coutbi;couta:;for(i=0;i5;i+)coutai;in.seekg(20);/定位准备读原来b的内容in.read(char*)b,20);/恢复b原来的内容coutb:;for(i=0;i5;i+)coutbi;,文件读写综合实例,