630 likes | 906 Views
第 9 章 流类库与输入 / 输出. 教学内容: I/O 流类库的功能,常用的读写操作,格式化输入输出操作,插入符和提取符的重载,磁盘文件的输入和输出操作,字符串流操作。 教学要求: 理解文件流的打开、读写、关闭方法以及设备文件的使用 ; 掌握基本输入 / 提取操作和格式控制。. 第 9 章 流类库与输入 / 输出. 9.1 输入 / 输出标准流类 9.2 文件流类 9.3 串流类 9.4 控制符. 9.1 输入 / 输出标准流类. 9.1.1 输入 / 输出流的概念
E N D
第9章 流类库与输入/输出 • 教学内容: • I/O流类库的功能,常用的读写操作,格式化输入输出操作,插入符和提取符的重载,磁盘文件的输入和输出操作,字符串流操作。 • 教学要求: • 理解文件流的打开、读写、关闭方法以及设备文件的使用; • 掌握基本输入/提取操作和格式控制。
第9章 流类库与输入/输出 • 9.1 输入/输出标准流类 • 9.2 文件流类 • 9.3 串流类 • 9.4 控制符
9.1 输入/输出标准流类 • 9.1.1 输入/输出流的概念 • C++语言中没有输入/输出语句。C++的I/O是以字节流的形式实现的,每一个C++编译系统都带有一个面向对象的输入/输出软件包,这就是I/O流类库。其中,流是I/O流类的中心概念。到目前为止,我们一直在使用它。
所谓流,是指数据从一个对象流向另一个对象。在C++程序中,数据可以从键盘流入到程序中,也可以从程序中流向屏幕或磁盘文件,把数据的流动抽象为“流”。流在使用前要被建立,使用后要被删除,还要使用一些特定的操作从流中获取数据或向流中添加数据。从流中获取数据的操作称为提取操作,向流中添加数据的操作称为插入操作。所谓流,是指数据从一个对象流向另一个对象。在C++程序中,数据可以从键盘流入到程序中,也可以从程序中流向屏幕或磁盘文件,把数据的流动抽象为“流”。流在使用前要被建立,使用后要被删除,还要使用一些特定的操作从流中获取数据或向流中添加数据。从流中获取数据的操作称为提取操作,向流中添加数据的操作称为插入操作。
9.1.2 输入/输出标准流类 • 1.标准流的设备名 • 由表9-1可见,I/O流的标准头文件是iostream.h。其中,ostream类通过其派生类ostream_withassign支持以下预先定义的流对象: • cout:标准输出。默认设备为屏幕。 • cerr:标准错误输出。没有缓冲,发送给它的内容立即被输出,默认设备为屏幕。
clog:标准错误输出。有缓冲,当缓冲区满时被输出,默认设备为打印机。 • 而istream类通过其派生类istream_withassign支持预先定义的对象。 • cin:标准输入。默认设备为键盘。
2.原理 • cout是ostream类的全局对象,它在头文件iostream.h中的定义如下: • ostream cout(stdout); //这里,stdout作为该对象构造时的参数对应每种基本数据类型,ostream类都存在友元,它们都在iostream.h中声明。例如: • ostream& operator<<(int n); • ostream& operator<<(float f); • ostream& operator<<(const char*psz); • //...
如语句: • cout<<"How old are you? "; • cout是类ostream的对象,<<是插入运算符,右面是char*类型,所以,应该匹配上面第三个操作符。它将整个字符串输出,并返回ostream流对象的引用。 • 同理,cin是istream的全局对象,istream类也存在友元。例如: • istream& operator>>( int &n); • istream& operator>>( float &f); • istream& operator>>( char*psz); • //...
9.2 文件流类 • fstream、ifstream和ofstream是文件流类,在头文件fstream.h中定义。其中,fstream是ofstream和ifstream多重继承的子类。文件流类不是标准设备,没有cout那样预先定义的全局对象。文件流类支持对磁盘文件的操作。要定义一个文件流类对象,须指定文件名和打开方式。
类ofstream用于执行文件输出,该类有以下几个构造函数:类ofstream用于执行文件输出,该类有以下几个构造函数: • ofstream::ofstream( filedesc fd ); • ofstream::ofstream( filedesc fd, char*pch, int nLength ); • ofstream::ofstream( const char*szName, • int nMode = ios::out, • int nProt = filebuf::openprot );
类ifstream用于执行文件输入,该类有以下几个构造函数:类ifstream用于执行文件输入,该类有以下几个构造函数: • ifstream::ifstream( filedesc fd ); • ifstream::ifstream( filedesc fd, char*pch, int nLength ); • ifstream::ifstream( const char*szName, • int nMode = ios::in, • int nProt = filebuf::openprot );
其中最常用的都是最后一个构造函数。该函数有三个参数,第一个参数是指向要打开的文件名的字符串,后两个参数指定文件的打开模式。文件打开模式的具体标志见表9-2。可以用按位OR(|)运算符组合这些标志,它们作为枚举器定义在ios类中。其中最常用的都是最后一个构造函数。该函数有三个参数,第一个参数是指向要打开的文件名的字符串,后两个参数指定文件的打开模式。文件打开模式的具体标志见表9-2。可以用按位OR(|)运算符组合这些标志,它们作为枚举器定义在ios类中。
打开一个输出文件,用于在文件尾添加数据 • 打开一个现存文件(用于输入或输出)并查找到结尾 • 打开一个输入文件。对于一个ofstream文件,使用ios::in作为一个openmode,可避免删除一个现存文件中现有的内容 • 打开一个文件,用于输出。对于所有ofstream对象,此模式是隐含指定的 • 如果一个文件存在,则打开它;否则该操作失败
如果一个文件不存在,则作为新文件打开它;如果文件已存在,则该操作失败如果一个文件不存在,则作为新文件打开它;如果文件已存在,则该操作失败 • 打开一个文件。如果它已经存在,则删除其中原有的内容。如果指定了ios::out,但没有指定ios::ate、ios::app和ios::in,则隐含为此模式 • 以二进制模式打开一个文件(默认是文本模式) • Nprot是文件保护方式,它的标志如表9-3。
【例9-1】 向文件myfile中写入一些信息。 • #include<fstream.h> • void main( ) • { • ofstream fc("c:\\temp\\myfile"); • fc<<"Constructs an ofstream object.\n" • <<"All ofstream constructors construct a filebuf object. \n"; • }
注意: • ① 这里的文件名要说明其路径,要使用双斜杠,因为C++编译器理解单斜杠为字符转换符。 • ② 在文件打开时,匹配了构造函数ofstream::ofstream(char*),只需要一个文件名,其它为默认。打开方式默认为ios::out | ios::trunc,即该文件用于接受程序的输出。如果该文件已存在,则其内容必须先清除,否则就新建。
如果要检查文件是否打开,则须判断成员函数fail( ): • #include <fstream.h> • void func( ) • { • ofstream fc("myfile"); • if(fc.fail( )) //fail( )= =1 • { • cerr<<"error opening file\n"; • return; • } • fc<<"...";
} • 若要打开一个输入文件,则有 • //... • ifstream fc("myfile",ios::nocreate); • //... • 当然,也可以通过检查fc.fail( )来确定文件打开是否出错。 • 如果要打开一个同时用于输入和输出的文件,则有 • //... • fstream fc("myfile",ios::in | ios::out); • //...
9.3 串流类 • strstream、istrstream和ostrstream是串流类,在头文件strstrea.h中定义。其中,strstream是istrstream和ostrstream多重继承的子类。同样,串流类也不是标准设备,它没有cout那样预先定义的全局对象。串流类允许将fstream类定义的文件操作应用于存储区中的字符串,即将字符串看作为设备。要定义一个串流类对象,须提供字符数组和数组大小。
类ostrstream用于执行串流输出,该类有以下几个构造函数:类ostrstream用于执行串流输出,该类有以下几个构造函数: • ostrstream( ); • ostrstream( char*pch, int nLength, int nMode = ios::out ); • 其中比较常用的是第二个构造函数,它有三个参数。第一个参数指出字符数组,第二个参数说明数组的大小,第三个参数指出打开方式。
类istrstream用于执行串流输入,该类有以下几个构造函数:类istrstream用于执行串流输入,该类有以下几个构造函数: • istrstream( char*pch ); • istrstream( char*pch, int nLength ); • 这两个构造函数都比较常用。Char*pch参数指出了字符数组,int nLength参数说明数组的大小。当nLength为0时,表示把istrstream类对象连接到由pch指向的以空字符结束的字符串。
例如,下面的程序代码定义一个串流类对象,并对其进行输入操作:例如,下面的程序代码定义一个串流类对象,并对其进行输入操作: • char str[50]="How are you!\n"; • char a; • istrstream ss(str); • ss>>a; • cout<<a<<endl; • 输出结果为 • H
【例9-2】 使用串流输入对字符串中的数据进行解读。 • #include<iostream.h> • #include<strstrea.h> • char *ioString(char*); • void main( ) • { • char *str="100 123.456"; • char *Buf0=ioString(str); • cout<<Buf0<<endl; • }
char *ioString(char *pString) • { • istrstream inS(pString,0); //以ios::in方式 • int iNumber; • float fNumber; • inS>>iNumber>>fNumber; //从串流中读入一个整数和浮点数 • char*Buf1=new char[28]; • ostrstream outS(Buf1,28); • outS<<"iNumber="<<iNumber • <<",fNumber="<<fNumber<<endl; • return Buf1;
} • 程序运行结果为 • iNumber=100,fNumber=123.456 • 分析:在函数ioString( )中,以pString为输入设备,先定义一个输入串流对象inS,从中输入一个整数和一个浮点数。再开辟一个字符串空间作为输出设备,定义一个输出串流对象outS,将从输入设备输入的两个变量的值输出。
9.4 控制符 • 9.4.1 使用流对象的成员函数 • 【例9-3】 使用width成员函数控制输出宽度。 • #include <iostream.h> • void main( ) • { • double values[ ]={1.44,36.47,625.7,4096.24}; • for(int i=0;i<4;i++) • {
cout.width(10); • cout<<values[i]<<'\n'; • } • } • 程序运行结果为 • 1.44 • 36.47 • 625.7 • 4096.24
此例子在一列中以至少10个字符宽按右对齐方式输出数据。从程序的输出结果可以看到,在少于10个字符宽的数值前加入了引导空格。此例子在一列中以至少10个字符宽按右对齐方式输出数据。从程序的输出结果可以看到,在少于10个字符宽的数值前加入了引导空格。 • 空格是默认的填充符,当输出的数据不能充满指定的宽度时,系统会自动以空格填充。另外,还可以使用fill成员函数为已经指定宽度的域设置填充字符的值。为了用星号填充数值列,我们可以将例9-3中的for循环修改如下:
for(int i=0;i<4;i++) • { • cout.width(10); • cout.fill('*'); • cout<<values[i]<<endl; • } • 其输出结果为 • ******1.44 • *****36.47 • *****625.7 • ***4096.24
9.4.2 使用控制符 • C++的输入/输出流类库提供了一些控制符,可以直接嵌入到输入/输出语句中来实现对I/O格式的控制。它的优点是程序可以直接将控制符插入流中,而不必单独调用。表9-4中列出了常用的I/O流类库控制符。
【例9-4】 使用setw控制符指定宽度。 • #include<iostream.h> • #include<iomanip.h> • void main( ) • { • double values[ ]={1.44,36.47,625.7,4096.24}; • char *names[ ]={"Rose","John","Alice","Mary"}; • for(int i=0;i<4;i++)
cout<<setw(6)<<names[i]<<setw(10)<<values[i]<<endl; • } • width成员函数在头文件iostream.h中说明。 如果带参量使用setw(n)或任何其它控制符,还必须包括头文件iomanip.h。在输出中,字符串输出在宽度为6的域中,整数输出在宽度为10的域中。程序运行结果为 • Rose 1.44 • John 36.47 • Alice 625.7 • Mary 4096.24
setw和width都不截断数值。如果一个数值需要比set(n)确定的字符数更多的字符,则该值将使用它所需要的所有字符。当然,还要遵守该流的精度设置。setw和width仅影响紧随其后的域,即使用setw和width设置的间隔方式并不保留其效力。在一个域输出完后,域宽度恢复成它的默认值 (必要的宽度),但其它流格式选项保持有效直到发生改变。
例如,下面的程序代码: • //... • cout <<setiosflags(ios::right) //设置为默认的右对齐方式, • <<setw(5)<<1 • <<setw(5)<<2 • <<setw(5)<<3<<endl; • cout <<setiosflags(ios::left) //设置成左对齐方式
<<setw(5)<<1 • <<setw(5)<<2 • <<setw(5)<<3<<endl; • cout<<resetiosflags(ios::left) //关闭左对齐标志 • //... • 这段程序代码中,是通过使用带参数的setiosflags控制符来设置左、右对齐,参数是ios::left和ios::right枚举器。
该枚举器定义在ios类中,因此,引用时必须包括ios::前缀。这里需要用resctiosflags操纵符关闭左、右对齐标志。setiosflags不同于width和setw,它的影响是持久的,直到用resetiosflags重新恢复默认值时为止。该枚举器定义在ios类中,因此,引用时必须包括ios::前缀。这里需要用resctiosflags操纵符关闭左、右对齐标志。setiosflags不同于width和setw,它的影响是持久的,直到用resetiosflags重新恢复默认值时为止。 • 这段程序代码的输出结果为 • 1 2 3 • 1 2 3 • 常用控制符和流格式控制成员函数如表9-5所示。
9.5 输入/输出成员函数 • 9.5.1 使用成员函数输入 • 1.getline( )函数 • 在程序使用cin输入时,cin用空白符和行结束符将各个值分开。有时候输入可能需要读取一整行文本并且分开不同的域,为此,我们可以使用getline成员函数。其函数原型如下: • istream&getline( char*pch, int nCount, char delim = '\n' );
其中,第一个参数是字符数组,用于放置读取的文本;第二个参数是本次读取的最大字符个数;第三个参数是分隔字符,作为读取一行结束的标志。其中,第一个参数是字符数组,用于放置读取的文本;第二个参数是本次读取的最大字符个数;第三个参数是分隔字符,作为读取一行结束的标志。 • getline成员函数的功能是允许从输入流中读取多个字符 (包括空白字符和行结束符),并且允许指定输入终止字符 (默认值是换行字符)。在读取完成后,从读取的内容中删除该终止字符。
【例9-5】 为输入流指定一个终止字符。 • 本程序连续读入一串字符,直到遇到字符t时停止,字符个数最多不超过99个。程序中的t是大小写敏感的。 • #include<iostream.h> • void main( ) • { • char line[100]; • cout<<"Type a line terminated by 't'"<<endl; • cin.getline(line,100,'t'); • cout<<line<<endl; • }
2.get( )函数 • 在输入时,有些时候需要执行每次只输入单个字符的操作,我们可以使用get( )成员函数来完成。get( )函数的格式如下: • char istream::get( ); • 【例9-6】 循环读入字符,直到键入一个y字符,或遇到文件尾。 • #include<iostream.h> • void main( ) • { • char letter; • while(!cin.eof( ))
{ • letter=cin.get( ); • if(letter=='y') • { • cout<<"'y'be met!"; • break; • } • cout<<letter; • } • }