《C++ 程序设计课件:第九章流类库和输入输出》由会员分享,可在线阅读,更多相关《C++ 程序设计课件:第九章流类库和输入输出(79页珍藏版)》请在金锄头文库上搜索。
1、第九章第九章 流类库和输入流类库和输入/ /输出输出本本章章的的最最主主要要目目的的就就是是把把对对象象保保存存到到磁磁盘盘文文件件中中并并从从磁盘文件重建对象磁盘文件重建对象。C+语言中并没有输入语言中并没有输入/输出语句,而是在标准库输出语句,而是在标准库里包含了一个里包含了一个I/O流类库,它与标准模板库同为流类库,它与标准模板库同为C+标标准库中最重要的组成部分。数据从一个对象到另一个对准库中最重要的组成部分。数据从一个对象到另一个对象的传送被抽象为象的传送被抽象为“流流”。数据的输入。数据的输入/输出就是通过输输出就是通过输入入/输出流来实现的。输出流来实现的。 流是一种抽象的概念,
2、负责在数据的产生者和数据流是一种抽象的概念,负责在数据的产生者和数据的使用者之间建立联系,并管理数据的流动。的使用者之间建立联系,并管理数据的流动。 第九章第九章 流类库和输入流类库和输入/ /输出输出9.1 C+的基本流类体系的基本流类体系 9.3 标准设备的输入标准设备的输入/输出输出 9.2 输入输出的格式控制(选读)输入输出的格式控制(选读) 9.4 文件的输入与输出文件的输入与输出 9.5 字符串流(选读)字符串流(选读) 9.6 文件与对象文件与对象 9.1 C+的基本流类体系的基本流类体系流类体系:流类体系:以抽象类模板以抽象类模板basic_ios为基类,流类模板派生体系见图为
3、基类,流类模板派生体系见图9.1。整个流类模板体系的。整个流类模板体系的标准标准I/O在头文件在头文件中说明,它包含中说明,它包含头文件头文件、和和。而输入输出。而输入输出文件流文件流部分在头文件部分在头文件中说明。中说明。 图图9.1 主要输入主要输入/输出流模板层次输出流模板层次 basic_ iosbasic_ streambufbasic_ istreambasic_ ostreambasic_ ifstreambasic_ iostreambasic_ ofstreambasic_ fstream指针指针9.1 C+的基本流类体系的基本流类体系 basic_ios类模板提供了对流进行
4、格式化输入输出和错类模板提供了对流进行格式化输入输出和错误处理的成员函数。所有派生都是公有派生。误处理的成员函数。所有派生都是公有派生。basic_istream类模板提供完成提取(输入)操作的成员函类模板提供完成提取(输入)操作的成员函数,而数,而basic_ostream类模板提供完成插入(输出)操作类模板提供完成插入(输出)操作的成员函数。的成员函数。basic_iostream类本质上是前两者的类本质上是前两者的聚合聚合,并没有增加成员。派生全部为公有派生并没有增加成员。派生全部为公有派生。 有关有关模板派生模板派生请参见教学指导书第请参见教学指导书第8章中章中“类模板的派类模板的派生
5、的讨论生的讨论” basic_streambuf不是不是basic_ios的派生类,而是一个的派生类,而是一个独立的类,只是独立的类,只是basic_ios有一个保护访问限制的指针指向有一个保护访问限制的指针指向它。它。 类类basic_streambuf的作用是管理一个流的缓冲区。的作用是管理一个流的缓冲区。流类体系说明:流类体系说明:9.1 C+的基本流类体系的基本流类体系标准输入标准输入/ /输出流对象:输出流对象: 在在C+C+的流类库的流类库中定义了四个全局流对象:中定义了四个全局流对象:cin,cout,cerr和和clog。可以完成人机交互的功能。可以完成人机交互的功能。 cin
6、标准输入流对象,键盘为其对应的标准设备。标准输入流对象,键盘为其对应的标准设备。 cout标准输出流对象,显示器为标准设备。标准输出流对象,显示器为标准设备。 cerr和和clog标准错误输出流,输出设备是显示器。标准错误输出流,输出设备是显示器。 其中其中cin、cout和和clog是带缓冲区的,缓冲区由是带缓冲区的,缓冲区由streambuf类对象来管理。而类对象来管理。而cerr为为非缓冲区流,一旦非缓冲区流,一旦错误发生立即显示。错误发生立即显示。 要使用这四个功能,必须包含要使用这四个功能,必须包含 文件。文件。 9.1 C+的基本流类体系的基本流类体系重载的重载的提取运算符提取运算
7、符“”(stream_extraction operator)和)和插入运算符插入运算符“和和(istream&,className&);friend ostream&operator”或或“”和和“”或或“”的左操作数;第二个参数为用户定义类的引用,的左操作数;第二个参数为用户定义类的引用,作为右操作数。作为右操作数。流用作函数参数,必须是引用调用,不能是传值流用作函数参数,必须是引用调用,不能是传值调用。因为这里要求处理流本身,而不是副本。调用。因为这里要求处理流本身,而不是副本。【例例9.5】改进自定义字符串类,重载插入运算符改进自定义字符串类,重载插入运算符“”。【例例9.6】用户定义
8、的复数类用户定义的复数类Complex的输入与输出。的输入与输出。 9.4 文件的输入与输出文件的输入与输出 本本节节中中文文件件指指的的是是磁磁盘盘文文件件。C+根根据据文文件件(file)内内容容的的数数据据格格式式,可可分分为为两两类类:二二进进制制文文件件和和文文本本文文件件。文文本本文文件件由由字字符符序序列列组组成成,也也称称ASCII码码文文件件,在在文文本本文文件件中中存存取取的的最最小小信信息息单单位位为为字字符符(character),而而二二进进制制文文件件中中存存取的最小信息单位为字节(取的最小信息单位为字节(Byte)。)。C+把每一个文件都看成一个有序的字节流,见图
9、把每一个文件都看成一个有序的字节流,见图9.2,每一个文件或者以文件结束符(每一个文件或者以文件结束符(end of file marker)结束,结束,或者在特定的字节号处结束。或者在特定的字节号处结束。 012436578n-1文件结束符文件结束符图图9.2 C+把文件看作有序的把文件看作有序的n个字节的流个字节的流文件的基本概念:文件的基本概念:9.4 文件的输入与输出文件的输入与输出当打开一个文件时,该文件就和某个流关联起来了。对文件进行读当打开一个文件时,该文件就和某个流关联起来了。对文件进行读写实际上受到一个写实际上受到一个文件定位指针(文件定位指针(file position p
10、ointer)的控制。的控制。 输入流的指针也称为读指针,每一次提取操作将从读指针当前输入流的指针也称为读指针,每一次提取操作将从读指针当前所指位置开始,每次提取操作自动将读指针向文件尾移动。所指位置开始,每次提取操作自动将读指针向文件尾移动。 输出流指针也称写指针,每一次插入操作将从写指针当前位置输出流指针也称写指针,每一次插入操作将从写指针当前位置开始,每次插入操作自动将写指针向文件尾移动。开始,每次插入操作自动将写指针向文件尾移动。9.4.1 文件的打开与关闭文件的打开与关闭 9.4.2 文本文件的读写文本文件的读写9.4.3 二进制文件的读写二进制文件的读写 9.4.4 文件的随机访问
11、(选读)文件的随机访问(选读) 9.4.1 文件的打开与关闭文件的打开与关闭文件使用步骤:文件使用步骤:1说明一个文件流对象,这又被称为内部文件:说明一个文件流对象,这又被称为内部文件:ifstream ifile;/只输入用只输入用ofstream ofile;/只输出用只输出用fstream iofile;/既输入又输出用既输入又输出用 9.4.1 文件的打开与关闭文件的打开与关闭2使使用用文文件件流流对对象象的的成成员员函函数数打打开开一一个个磁磁盘盘文文件件。这这样样在在文文件件流流对对象象和和磁磁盘盘文文件件名名之之间间建建立立联联系系。文文件件流流中中说说明明了了三三个个打打开文件
12、的成员函数。开文件的成员函数。void ifstream:open(const char*,int =ios:in, int=filebuf:openprot);void ofstream:open(const char *,int=ios:out, int=filebuf:opernprot);void fstream:open(const char*,int, int=filebuf:openprot); 第一个参数为要打开的磁盘文件名。第二个参数为打开方式,第一个参数为要打开的磁盘文件名。第二个参数为打开方式,有输入(有输入(in),输出(),输出(out)等,打开方式在)等,打开方式在
13、ios基类中定义为基类中定义为枚举类型。第三个参数为指定打开文件的保护方式,一般取默枚举类型。第三个参数为指定打开文件的保护方式,一般取默认。认。所以第二步可如下进行:所以第二步可如下进行:iofile.open(“myfile.txt”,ios:in|ios:out);文件打开方式文件打开方式: :是由在是由在ios类中定义的公有枚举成员决定:类中定义的公有枚举成员决定:enum open_mode in=0x01,out=0x02,ate=0x04,app=0x08,trunc=0x10,binary=0x80;9.4.1 9.4.1 文件的打开与关闭文件的打开与关闭 inin标识打开文件
14、用于输入操作标识打开文件用于输入操作( (从文件读取从文件读取) )。打开方式只要含。打开方式只要含inin,如文件不存在则返回失败。在打开为输入输出方式时(同时用,如文件不存在则返回失败。在打开为输入输出方式时(同时用outout),),编程应注意判断是否失败,失败时千万不可再写入文件。编程应注意判断是否失败,失败时千万不可再写入文件。 outout标识打开标识打开文件用于输出操作文件用于输出操作( (写入文件写入文件) )。如文件不存在,则建。如文件不存在,则建立新文件,如文件存在,未同时设立新文件,如文件存在,未同时设app, inapp, in则文件清空。则文件清空。 trunctru
15、nc标识打开文件,并清空它(文件长度截为标识打开文件,并清空它(文件长度截为0 0)。文件不存在)。文件不存在则建立新文件,与则建立新文件,与outout默认操作相同。但与默认操作相同。但与inin配合,文件不存在则返回配合,文件不存在则返回失败。失败。 appapp标识打开文件用于输出,原文件内容保留,新数据接在尾部标识打开文件用于输出,原文件内容保留,新数据接在尾部 ateate意思是意思是at endat end,标识打开文件,文件指针在文件尾,但文件指,标识打开文件,文件指针在文件尾,但文件指针可以移动,即新数据可写到任何位置。文件是否清空由其它标识决针可以移动,即新数据可写到任何位置
16、。文件是否清空由其它标识决定。定。 后三个标识最好配合后三个标识最好配合outout、inin等一起用,因为不同的等一起用,因为不同的C+C+平台,要平台,要求不同,一起用不会出错。如不一起用,至少求不同,一起用不会出错。如不一起用,至少VC+VC+不认这种格式。不认这种格式。 binarybinary标识以二进制方式打开文件。同时用标识以二进制方式打开文件。同时用outout时,如文件不存在,时,如文件不存在,则建立新文件,并且新文件能用,不必清状态字。则建立新文件,并且新文件能用,不必清状态字。打开方式解释:9.4.1 文件的打开与关闭文件的打开与关闭 三三个个文文件件流流类类都都重重载载
17、了了一一个个带带默默认认参参数数的的构构造造函函数数,功能与,功能与open函数一样:函数一样:ifstream:ifstream(const char*, int=ios:in, int=filebuf:openprot);ofstream:ofstream(const char*,int=ios:out, int=filebuf:openprot);fstream:fstream(const char*,int, int=filebuf:operprot);所以所以1,2两步可合成:两步可合成:fstream iofile(”myfile.txt”,ios:in|ios:out);9.4.
18、1 文件的打开与关闭文件的打开与关闭打打开开文文件件也也应应该该判判断断是是否否成成功功,若若成成功功,文文件件流流对对象象值值为为非非零零值值,不不成成功功为为0(NULL),文文件件流流对对象象值值物理上就是指它的地址物理上就是指它的地址。因此打开一个文件完整的程序为:因此打开一个文件完整的程序为:fstream iofile(”myfile.txt”,ios:in|ios:out);if(!iofile) /“!”为重载的运算符,见为重载的运算符,见9.3.1节节 cout”不不能能打打开开文文件件:”myfile.txt”)和和插插入入运运算算符符(”完成完成重构重构对象,而只用一个对
19、象,而只用一个“”完成对象完成对象存入存入文件。文件。9.4.2 文本文件的读写文本文件的读写文本数据文件的读写:文本数据文件的读写:按按C+的的常常规规,资资源源获获取取是是由由构构造造函函数数实实现现,而而资资源源释释放放是是由由析析构构函函数数完完成成。所所以以与与内内存存动动态态分分配配一一样样,由由文文件件重重构构对对象象放放在在构构造造函函数数中中,把把对对象象存存入入文文件件则则放放在在析析构构函函数数中中。详详细细内内容容见见9.6节节,更更深深层层次次的的理理由由见见下下一一章章异异常常处处理理。【例例9.9】只只是是为为了了更更清清楚楚地地显显示示文文件件的的操操作作,常常
20、规规做做法法请请读读者者参参见见【例例9.13】。9.4.3 二进制文件的读写二进制文件的读写对二进制文件进行读写的成员函数:对二进制文件进行读写的成员函数:istream&istream:read(char *,int); /从二进制流提取从二进制流提取istream&istream:read(unsigned char*,int);/第一个参数指定存放有效输入的变量地址第一个参数指定存放有效输入的变量地址,必须必须强制转换强制转换为为char*类型类型/第二个参数指定提取的字节数第二个参数指定提取的字节数,/函数从输入流提取指定数量的字节送到指定地址开始的单元函数从输入流提取指定数量的字节
21、送到指定地址开始的单元ostream&ostream:write(const char *,int);/向二进制流插入向二进制流插入ostream&ostream:write(const unsigned char *,int);/第一个参数指定输出对象的内存地址第一个参数指定输出对象的内存地址,必须必须强制转换强制转换为为char*类型类型/第二个参数指定插入的字节数第二个参数指定插入的字节数,/函数从该地址开始将指定数量的字节插入输入输出流函数从该地址开始将指定数量的字节插入输入输出流9.4.3 二进制文件的读写二进制文件的读写文件结束判断:文件结束判断:读函数并不能知道文件是否结束,可用
22、状态函数读函数并不能知道文件是否结束,可用状态函数int ios:eof()来判断文件是否结束。必须指出系统是根据来判断文件是否结束。必须指出系统是根据当前操作当前操作的实际情况设置状态位,如需根据状态位来判断下一步的实际情况设置状态位,如需根据状态位来判断下一步的操作,必须在一次操作后立即去调取状态位,以判断的操作,必须在一次操作后立即去调取状态位,以判断本次操作是否有效。本次操作是否有效。【例例9.10】创建二进制数据文件,以及数据文件的读创建二进制数据文件,以及数据文件的读取。这两项操作设计为成员函数。给出与取。这两项操作设计为成员函数。给出与【例例9.9】不同不同的读写方式:的读写方式
23、:9.4.3 二进制文件的读写二进制文件的读写二进制文件优点:二进制文件优点: 可以控制字节长度,读写数据时不会出现二义可以控制字节长度,读写数据时不会出现二义性,性,可靠性高可靠性高。同时不知格式是无法读取的,。同时不知格式是无法读取的,保密保密性好性好。文件结束后,系统不会再读(见。文件结束后,系统不会再读(见eofbit的说明)的说明),但,但程序不会自动停下来程序不会自动停下来,所以要判断文件中是否,所以要判断文件中是否已没有数据。如写完数据后没有关闭文件,直接开已没有数据。如写完数据后没有关闭文件,直接开始读,则必须把文件定位指针移到文件头。如始读,则必须把文件定位指针移到文件头。如
24、关闭关闭文件后重新打开,文件定位指针就在文件头文件后重新打开,文件定位指针就在文件头。9.4.4 文件的随机访问(选读)文件的随机访问(选读)文件的随机访问:文件的随机访问:在在C+C+中可以由程序控制文件指针的移动,从而实现文件的中可以由程序控制文件指针的移动,从而实现文件的随机访问随机访问,即可,即可读写流中任意一段内容读写流中任意一段内容。一般文本文件很难准。一般文本文件很难准确定位,所以随机访问多用于二进制文件。确定位,所以随机访问多用于二进制文件。如如【例例9.99.9】中对象中两个字符串是按实际串长存放的,不是按中对象中两个字符串是按实际串长存放的,不是按数组元素来存放的,而数组元
25、素来存放的,而【例例9.109.10】中是按数组长度来存放的,中是按数组长度来存放的,每个对象数据长度固定,所以便于随机访问。每个对象数据长度固定,所以便于随机访问。在在ios类中说明了一个公有枚举类型:类中说明了一个公有枚举类型:enum seek_dirbeg=0, /文件开头文件开头cur=1, /文件指针的当前位置文件指针的当前位置end=2 /文件结尾文件结尾;随机访问指针控制字:随机访问指针控制字:9.4.4 文件的随机访问(选读)文件的随机访问(选读)输入流指针控制字设置成员函数:输入流指针控制字设置成员函数:istream&istream:seekg(streampos); /
26、指针直接定位指针直接定位istream&istream:seekg(streamoff, ios:seek_dir); /指针相对定位指针相对定位long istream:tellg(); /返回当前指针位置返回当前指针位置流的指针位置类型流的指针位置类型streampos和流的指针偏移类型和流的指针偏移类型streamoff定定义为长整型,也就是可访问文件的最大长度为义为长整型,也就是可访问文件的最大长度为4G例:例:datafile.seekg(-20L,ios:cur);/表示将文件定位指针从当前位置向文件头部方向移表示将文件定位指针从当前位置向文件头部方向移20个字节。个字节。data
27、file.seekg(20L,ios:beg);/表示将文件定位指针从文件头向文件尾方向移表示将文件定位指针从文件头向文件尾方向移20个字节。个字节。datafile.seekg(-20L,ios:end);/表示将文件定位指针从文件尾向文件头方向移表示将文件定位指针从文件尾向文件头方向移20个字节。个字节。tellg()和和seekg()往往配合使用。往往配合使用。/指针不可移到文件头之前或文件尾之后。指针不可移到文件头之前或文件尾之后。9.4.4 文件的随机访问(选读)文件的随机访问(选读)输入流指针控制字设置成员函数:输入流指针控制字设置成员函数:ostream&ostream:seek
28、p(streampos);ostream&ostream:seekp(streamoff,ios:seek_dir);long ostream:tellp(); 为了便于记忆,函数名中为了便于记忆,函数名中g是是get的缩写,而的缩写,而p是是put的的缩写。缩写。对输入输出文件定位指针只有一个但函数有两组对输入输出文件定位指针只有一个但函数有两组,这,这两组函数功能完全一样。两组函数功能完全一样。 【例例9.11】使用随机访问对使用随机访问对【例例9.10】进行改造。进行改造。9.5 字符串流(选读)字符串流(选读)字符流概念:字符流概念: 字符串(字符串(string)也可以看作)也可以看
29、作字符流字符流。可以用输入输出操。可以用输入输出操作来完成串流的操作。串流与内存相关,所以也称作来完成串流的操作。串流与内存相关,所以也称内存流内存流。 串流类包括串流类包括ostrstream、istrstream、strstream,它们在,它们在中说明。串流类对象可以保存字符,也可以保存整中说明。串流类对象可以保存字符,也可以保存整数、浮点数。串流类对象采用数、浮点数。串流类对象采用文本方式文本方式。其构造函数常用下面几个:其构造函数常用下面几个:istrstream:istrstream(const char * str);istrstream:istrstream(const cha
30、r * str,int);ostrstream:ostrstream(char *,int,int=ios:out);strstream:strstream(char *,int,int); 其中第二个参数说明数组大小,第三参数为文件打开方式其中第二个参数说明数组大小,第三参数为文件打开方式。【例例9.12】9.6 文件与对象文件与对象在面向对象的在面向对象的C+程序设计中,文件应该程序设计中,文件应该在构造函数中打开在构造函数中打开,并并创建对象创建对象;而在;而在析构函数中保存和关闭文件,并撤销对象析构函数中保存和关闭文件,并撤销对象。当撤销对象时,能自动释放资源。释放资源包括将对象中的信
31、当撤销对象时,能自动释放资源。释放资源包括将对象中的信息再次存入磁盘文件。程序运行中,总要对保存在对象的数据息再次存入磁盘文件。程序运行中,总要对保存在对象的数据成员里的信息进行操作,这时应该将信息适时保存到相应的磁成员里的信息进行操作,这时应该将信息适时保存到相应的磁盘文件中,以免数据意外丢失。这些都是常规操作,是面向对盘文件中,以免数据意外丢失。这些都是常规操作,是面向对象的象的C+程序设计的固定框架。程序设计的固定框架。 规范化操作:规范化操作:在面向对象的程序设计中,信息总是放在对象在面向对象的程序设计中,信息总是放在对象的数据成员里。这些信息最终应该保存到文件的数据成员里。这些信息最
32、终应该保存到文件中。当程序开始运行时要由打开的文件重新创中。当程序开始运行时要由打开的文件重新创建对象。在运行过程中,放在对象的数据成员建对象。在运行过程中,放在对象的数据成员里的信息得到利用和修改。运行结束时必须把里的信息得到利用和修改。运行结束时必须把这些信息重新保存到文件中,然后关闭文件。这些信息重新保存到文件中,然后关闭文件。9.6 文件与对象文件与对象【例例9.13】将商店的货物,定义为一个货物数组类。数将商店的货物,定义为一个货物数组类。数组对象动态建立,初始为组对象动态建立,初始为2个元素,不够用时扩充一倍。用个元素,不够用时扩充一倍。用文本数据文件建立数组元素对象,要求放在构造
33、函数中,文本数据文件建立数组元素对象,要求放在构造函数中,而数据的保存和文件的关闭放在析构函数中。第一次运行而数据的保存和文件的关闭放在析构函数中。第一次运行时,建立空的数据文件,由键盘输入建立数组元素对象,时,建立空的数据文件,由键盘输入建立数组元素对象,并写入文件,程序退出时,关闭文件;下一次运行由该文并写入文件,程序退出时,关闭文件;下一次运行由该文件构造对象,恢复前一次做过的工作。件构造对象,恢复前一次做过的工作。这是一个标准的面向对象的程序设计,也是对前面各章内容这是一个标准的面向对象的程序设计,也是对前面各章内容的小结。注意,本例使用了的小结。注意,本例使用了多重的插入运算符重载多
34、重的插入运算符重载。 第九章第九章 流类库和输入流类库和输入/ /输出输出结束结束谢谢!谢谢!9.2 输入输出的格式控制(选读)输入输出的格式控制(选读)访问访问x_flags的重载函数声明如下:的重载函数声明如下:inline long ios:flags() const; /返回当前标志字返回当前标志字inline long ios:flags(long ); /把参数作为新的标志字把参数作为新的标志字,并返回原标志字并返回原标志字(下同下同)inline long ios:setf(long ,long ); /在原标志字上增加控制在原标志字上增加控制, /第一个参数为增加的控制第一个参
35、数为增加的控制,第二个参数表示取代原来哪几位第二个参数表示取代原来哪几位inline long ios:setf(long ); /增加控制增加控制(多项多项)inline long ios:unsetf(long ); /清除指定位上的控制清除指定位上的控制9.2 输入输出的格式控制输入输出的格式控制(选读)(选读)相关接口函数为:相关接口函数为:inline int ios:width() const; /返回当前域宽返回当前域宽inline int ios:width(int ); /把参数作为新的域宽把参数作为新的域宽,返回原域宽返回原域宽inline char ios:fill()
36、const; /返回当前填充字符返回当前填充字符inline char ios:fill(char );/参数为新填充字符参数为新填充字符,返回原填充字返回原填充字符符inline int ios:precision(int ); /参数作为新精度参数作为新精度,返回原精度返回原精度inline int ios:precision() const; /返回当前精度返回当前精度9.2 输入输出的格式控制(选读)输入输出的格式控制(选读)【例例9.1】整型数输出。整型数输出。#includeusing namespace std;int main(void)int inum=255;cout十进制
37、方式十进制方式inumt;cout.flags(ios:oct|ios:showbase); /八进制带数制基输出是前面加八进制带数制基输出是前面加0;对对VC+参数等效参数等效0x00a0cout八进制方式八进制方式inumt;cout.setf(ios:hex,ios:basefield );/basefield代表控制代表控制 /进位基制的三位进位基制的三位ios:dec|ios:oct|ios:hex。 /因是或关系因是或关系,仍带基输出仍带基输出,格式为格式为0x.cout十六进制方式十六进制方式inumendl;return 0;程序输出:程序输出:十进制方式十进制方式255 八进
38、制方式八进制方式0377 十六进制方式十六进制方式0xff9.2 输入输出的格式控制(选读)输入输出的格式控制(选读)【例例9.2】浮点数输出。浮点数输出。int main()double fnum=31.415926535;cout默认域宽为默认域宽为:cout.width()位位n; cout默认精度为默认精度为:cout.precision()位位n; cout默认表达方式默认表达方式:fnumn; /按值大小按值大小,自动决定定点还是科学数方式自动决定定点还是科学数方式cout.setf(ios:scientific,ios:floatfield); cout科学数表达方式科学数表达方
39、式:fnumn;cout.setf(ios:fixed,ios:floatfield); /设为定点设为定点,取消科学数方式取消科学数方式cout定点表达方式定点表达方式:fnumn;cout.precision(9); /精度精度9位指小数点后位指小数点后9位位cout.setf(ios:scientific,ios:floatfield); cout 9位科学数表达方式位科学数表达方式fnumn;return 0;其中其中floatfield代表浮点数的两控代表浮点数的两控制位制位ios:scientific|ios:fixed为了避免浮点数互相冲突的双为了避免浮点数互相冲突的双重规定,所
40、以先重规定,所以先清除原浮点数清除原浮点数两个输出控制位两个输出控制位,再用新的一,再用新的一位去代替位去代替。9.2 输入输出的格式控制(选读)输入输出的格式控制(选读)流操作子(流操作子(setiosflags stream manipulator)可代替流格式)可代替流格式控制成员函数。控制成员函数。它们的使用格式如第一章所介绍的它们的使用格式如第一章所介绍的setw()等使等使用的格式。注意,绝大多数流操作子仅适用于新的用的格式。注意,绝大多数流操作子仅适用于新的C+标准流标准流类库(头文件不带类库(头文件不带.h)。)。操作符操作符含含义义boolapha把把true和和false表
41、示表示为为字符串字符串*noboolalpha把把true和和false表示表示为为0、1showbase产产生前生前缀缀,指示数,指示数值值的的进进制基数制基数*noshowbase不不产产生生进进制基数前制基数前缀缀showpoint总总是是显显示小数点示小数点*noshowpoint只有当小数部分存在是才只有当小数部分存在是才显显示小数点示小数点操作符操作符含含义义showpos在非在非负负数数值值中中显显示示+*noshowpos在非在非负负数数值值中不中不显显示示+*skipws输输入操作符跳入操作符跳过过空白字符空白字符noskipws输输入操作符不跳入操作符不跳过过空白字符空白字
42、符uppercase在十六在十六进进制下制下显显示示0X,科学,科学计计数法中数法中显显示示E*nouppercase在十六在十六进进制下制下显显示示0x,科学,科学计计数法中数法中显显示示e*dec以十以十进进制制显显示示(VC支持支持)hex以十六以十六进进制制显显示示(VC支持支持)oct以八以八进进制制显显示示(VC支持支持)left将填充字符加到数将填充字符加到数值值的右的右边边right将填充字符加到数将填充字符加到数值值的左的左边边internal将填充字符加到符号和数将填充字符加到符号和数值值的中的中间间*fixed以小数形式以小数形式显显示浮点数示浮点数scientific以
43、科学以科学计计数法形式数法形式显显示浮点数示浮点数flush 刷新刷新ostream缓缓冲区冲区(VC支持支持)操作符操作符含含义义ends插入字符串插入字符串结结束符,刷新束符,刷新ostream缓缓冲区冲区(VC支持支持)endl 插入插入换换行符,然后刷新行符,然后刷新ostream缓缓冲区冲区(VC支持支持)ws“吃掉吃掉”空白字符空白字符(VC支持支持)/以下以下这这些参数化的流操作子要求些参数化的流操作子要求#includesetfill(ch)用用ch填充空白字符填充空白字符(VC支持支持)setprecision(n)将浮点精度将浮点精度设设置置为为n (VC支持支持)setw
44、(n) 按照按照n个字符来个字符来读读或者写或者写(VC支持支持)setbase(b)以以进进制基数制基数b为输为输出整数出整数值值(VC支持支持)注:注:*表示默表示默认认的流状的流状态态cin,cout和和clog都是都是缓冲流缓冲流。对输出而言,仅当输出缓冲区满。对输出而言,仅当输出缓冲区满才将缓冲区中的信息输出,对输入而言,才将缓冲区中的信息输出,对输入而言,仅当输入一行结束,仅当输入一行结束,才开始从缓冲区中取数据才开始从缓冲区中取数据,当希望把缓冲区中的信息立即输出,当希望把缓冲区中的信息立即输出,可用可用flush,加,加endl也有同样功能,回车并立即显示,不必等缓也有同样功能
45、,回车并立即显示,不必等缓冲区满(冲区满(endl清空缓冲区)。清空缓冲区)。 9.2 输入输出的格式控制(选读)输入输出的格式控制(选读)【例例9.2_1】采用流操作子的浮点数输出。采用流操作子的浮点数输出。#include#includeusing namespace std;int main() double fnum=31.415926535; cout默认域宽为默认域宽为:cout.width()位位n; cout默认精度为默认精度为:cout.precision()位位n; cout默认表达方式默认表达方式:fnumn; /按值大小按值大小,自动决定定点还是科学数方式自动决定定点还
46、是科学数方式 cout科学数表达方式科学数表达方式:scientificfnumn; /设为科学数方式设为科学数方式 cout定点表达方式定点表达方式:fixedfnumn; /设为定点设为定点,取消科学数方取消科学数方式式 cout9位科学数表达方式位科学数表达方式setprecision(9)scientificfnumn; return 0;读取状态的有关操作如下:读取状态的有关操作如下:inline int ios:rdstate() const return state; /读取状态字读取状态字inline int ios:operator!() constreturn state&
47、(badbit|failbit); /可用操作符可用操作符!()代替代替fail()inline int ios:bad() return state & badbit; /返回非法操作位返回非法操作位inline void ios:clear(int _i) lock();state=_i;unlock(); /人工设置状态人工设置状态,可用来清状态可用来清状态inline int ios:eof() const return state&eofbit; /返回流返回流(文件文件)结束位结束位inline int ios:fail() constreturn state&(badbit|fa
48、ilbit); /返回操作非法和操作失败这两位返回操作非法和操作失败这两位inline int ios:good() constreturn state=0; /正常返回正常返回1,否则返回否则返回09.3.1 9.3.1 提高标准输入提高标准输入/ /输出的稳健性输出的稳健性【例例9.3】提高输入的稳健性。提高输入的稳健性。int main() char str256; int i; cout请输入整数请输入整数:i;/可故意输入若干非数字字符可故意输入若干非数字字符,下次再输入若干字符加数字串下次再输入若干字符加数字串 /加若干非数字字符进行检测加若干非数字字符进行检测 while(cin
49、.fail() cout状态字为:状态字为:cin.rdstate()endl; cin.clear(0); cin.getline(str,255); /读空缓冲区读空缓冲区 cout输入错误输入错误,请重新输入整数请重新输入整数i; cin.getline(str,256); /读空缓冲区读空缓冲区,吃掉回车符吃掉回车符 cout请输入字符串请输入字符串endl; cin.getline(str,255); cout输入整数为输入整数为:iendl; cout输入字符串为输入字符串为:strendl; return 0;9.3.2 9.3.2 标准输入标准输入/输出成员函数(选读)输出成员
50、函数(选读)【例例9.4】ignore()和和gcount()函数使用。函数使用。int main() char str255; int i,n; cout输入字符输入字符endl; /输入输入Z,一旦输入一旦输入Z全部结束,不能输入其它字符全部结束,不能输入其它字符 i=cin.get(); coutendl; n=cin.rdstate(); /读取状态字读取状态字 cout状态字为:状态字为:nendl; /状态字为状态字为1,流结束流结束 cout当输入字符时当输入字符时,取得的是取得的是:iendl; /-1,输入输入Z时时,返回返回EOF,即即-1 if(n=0) cin.igno
51、re(255,n); /清除多余的字符和回车符清除多余的字符和回车符 cin.clear(0); / A 使流恢复正常使流恢复正常 cout输入字符串输入字符串1:endl; cin.getline(str,255); coutendl;9.3.2 9.3.2 标准输入标准输入/输出成员函数(选读)输出成员函数(选读) cout状态字为:状态字为:cin.rdstate()endl; i=cin.gcount(); cout字符串为字符串为:strt读入字符数为读入字符数为:it; cout串长为串长为:strlen(str)endl; cin.clear(0); / A 使流恢复正常使流恢复
52、正常 cout输入字符串输入字符串2:endl; cin.getline(str,255); coutendl; cout状态字为:状态字为:cin.rdstate()endl; i=cin.gcount(); cout字符串为字符串为:strt读入字符数为读入字符数为:it; cout串长为串长为:strlen(str)endl; return 0;注意注意,若无两个,若无两个A行,输入行,输入Z后,不再理会余下的所有输入。后,不再理会余下的所有输入。9.3.3 9.3.3 重载插入和提取运算符重载插入和提取运算符【例例9.5】改进自定义字符串类,重载插入运算符改进自定义字符串类,重载插入运
53、算符“”:重载插入运算符重载插入运算符“”声明为声明为mystring的友元:的友元:friend ostream & operator(ostream & ,const mystring &); /流类作为形式参数必须是引用流类作为形式参数必须是引用定义为:定义为:ostream & operator(ostream & s,const mystring & cstr) return scstr.strt;本例中线性表本例中线性表Orderedlist的元素的元素Node的数据域的数据域key类型由模板参数类型由模板参数T决定,实例化后为决定,实例化后为mystring,打印函数为:,打印函
54、数为:template void Orderedlist:print() int i; for(i=0;i=last;i+)coutslisti.key;if(i%5=4) coutendl; coutendl;更重要的是不用更重要的是不用show()函数,格式一致了,可以同时用于基本数据类函数,格式一致了,可以同时用于基本数据类型,如整型、实型、字符型,也可用于标准字符串型,如整型、实型、字符型,也可用于标准字符串string。【例例9.6】用户定义的复数类型用户定义的复数类型Complex的输入与输出。的输入与输出。#includeusing namespace std;class Com
55、plex double Real,Image;public: Complex(double r=0.0, double i=0.0):Real(r),Image(i) /这里省略若干成员函数这里省略若干成员函数,以节约篇幅以节约篇幅,详见详见【例例4.7】 friend ostream&operator(istream&s,Complex&a); /流类作为形式参数必须是引用流类作为形式参数必须是引用;ostream&operator(ostream&s,const Complex &z) return s(z.Real,z.Image(istream&s,Complex &a) /格式为格式
56、为d,(d),(d,d) double re=0,im=0; char c=0; sc; if(c=()/是否由括号开始是否由括号开始 srec; /实部实部 if(c=,) simc; /虚部虚部 if(c!=) s.clear(ios:failbit); /漏了括号给一个操作失败标志漏了括号给一个操作失败标志 else s.putback(c); /无括号,返回一个字符到输入缓冲区无括号,返回一个字符到输入缓冲区 sre; /实数实数 if(s) a=Complex(re,im); /当流当流s正常,给正常,给a赋值赋值 return s;9.3.3 9.3.3 重载插入和提取运算符重载插
57、入和提取运算符putback()声明如下:声明如下:stream&istream:putback(char);它将最后一次从输入流中得到的字它将最后一次从输入流中得到的字符放回到输入流中符放回到输入流中。int main()Complex a,b,c;cout输入一个实数输入一个实数a;cout输入一个用括号括起来的实数输入一个用括号括起来的实数b;cout输入一个用括号括起来复数输入一个用括号括起来复数c;couta=atb=bt c=cn;return 0;9.3 9.3 标准设备的输入标准设备的输入/ /输出输出9.4.2 文本文件的读写文本文件的读写【例例9.7】复制文件。复制文件。
58、int main() char ch; ifstream sfile(d:Ex9_6Ex9_6.cpp); ofstream dfile(e:Ex9_6.cpp); /只能创建文件,不能建立子目录,如路径不存在则失败只能创建文件,不能建立子目录,如路径不存在则失败 if(!sfile) cout不能打开源文件不能打开源文件:d:Ex9_6Ex9_6.cppendl; return -1; if(!dfile) cout不能打开目标文件不能打开目标文件:e:Ex9_6.cppch)dfile”)运算符在)运算符在默认情况下是跳过默认情况下是跳过空白空白字符的,这样复制的文件会缺少一些字符的,这样
59、复制的文件会缺少一些字符。字符。第二第二,该程序能确定文件是否,该程序能确定文件是否复制结束复制结束。流类成员函数和运。流类成员函数和运算符全是返回本类型的引用,这里就是流文件对象自身,当算符全是返回本类型的引用,这里就是流文件对象自身,当文件结束时,返回文件结束时,返回NULLNULL,这时不再复制,退出循环。,这时不再复制,退出循环。第三第三,复制是,复制是按字节进行按字节进行的,效率很低,按字节传递开销极的,效率很低,按字节传递开销极大,但该程序能正确复制任意类型的文件,不仅是文本文件大,但该程序能正确复制任意类型的文件,不仅是文本文件(看作按字符),二进制文件(看作按字节)也一样可正确
60、(看作按字符),二进制文件(看作按字节)也一样可正确完成。如果是文本文件,我们可以按行进行复制。完成。如果是文本文件,我们可以按行进行复制。第四第四,! !sfilesfile中的!是重载的运算符,在状态函数中重载,中的!是重载的运算符,在状态函数中重载,当该操作出现不正常状态,返回当该操作出现不正常状态,返回操作非法和操作失败这两位操作非法和操作失败这两位 。【例例9.8】按行复制文本文件按行复制文本文件。int main() char filename256,buf100; fstream sfile,dfile; cout输入源文件路径名输入源文件路径名:filename; /对路径名而
61、言空格是无关紧要的对路径名而言空格是无关紧要的,否则要用否则要用getline()等成员函数等成员函数 sfile.open(filename,ios:in);/打开一个已存在的文件打开一个已存在的文件 while(!sfile)cout源文件找不到源文件找不到,请重新输入路径名请重新输入路径名:filename;sfile.open(filename,ios:in); cout输入目标文件路径名输入目标文件路径名:filename; /只能创建文件,不能建立子目录,如路径不存在则失败只能创建文件,不能建立子目录,如路径不存在则失败 dfile.open(filename,ios:out);
62、if(!dfile) cout目标文件创建失败目标文件创建失败endl; return -1; while(sfile.getline(buf,100),sfile.eof()!=1) /按行复制按行复制 A行行 if(sfile.rdstate()=0) dfilebufn; /因读到回车符,提取但未保存因读到回车符,提取但未保存 B行行 else dfile”完成重构对象,而只用一个完成重构对象,而只用一个“”完成对象存入文件。完成对象存入文件。 class inventory string Description; string No; int Quantity; double Cost
63、; double Retail;public: inventory(string=#,string=0,int=0,double=0,double=0); friend ostream&operator(istream&sour,inventory&iv); ; /流类作为形式参数必须是引用流类作为形式参数必须是引用9.4.2 文本文件的读写文本文件的读写inventory:inventory(string des,string no,int quan, double cost,double ret) Description=des; No=no; Quantity=quan; Cost=co
64、st; Retail=ret;ostream &operator(ostream&dest,inventory&iv) destleftsetw(20)iv.Descriptionsetw(10)iv.No; destrightsetw(10)iv.Quantitysetw(10)iv.Cost setw(10)iv.Retail(istream&sour,inventory&iv) souriv.Descriptioniv.Noiv.Quantity iv.Costiv.Retail; return sour; /从文件读出是自动把数字串转为数读出,从文件读出是自动把数字串转为数读出,/函数
65、体内函数体内功能不变,会自动转换功能不变,会自动转换9.4.2 文本文件的读写文本文件的读写int main() inventory car1(夏利夏利2000,805637928,156,80000,105000),car2; inventory motor1(金城金城125,93612575,302,10000,13000),motor2; ofstream destfile(d:Ex9_9.data); destfilecar1motor1; /注意注意ofstream是是ostream的派生类的派生类 destfile.close(); coutcar1; coutmotor1; co
66、utcar2; coutcar2motor2; sourfile.close(); coutcar2; cout和插入运和插入运算符算符。void inventory:Bdatatofile(ofstream&dest)dest.write(Description.c_str(),20); /由由string类的类的c_str()函数转为函数转为char*dest.write(No.c_str(),10);dest.write(char*)&Quantity,sizeof(int);/注意指针类型注意指针类型dest.write(char*)&Cost,sizeof(double);/强制转换
67、强制转换dest.write(char*)&Retail,sizeof(double);9.4.3 二进制文件的读写二进制文件的读写void inventory:Bdatafromfile(ifstream&sour)char k20;sour.read(k,20);Description=k;sour.read(k,10);No=k;sour.read(char*)&Quantity,sizeof(int);sour.read(char*)&Cost,sizeof(double);sour.read(char*)&Retail,sizeof(double); /读和写是完全对称的过程读和写是
68、完全对称的过程,次序决不能错次序决不能错9.4.3 二进制文件的读写二进制文件的读写int main() inventory car1(夏利夏利2000,805637928,156,80000,105000),car2; inventory motor1(金城金城125,93612575,302,10000,13000),motor2; ofstream ddatafile(d:Ex9_10.data,ios:out|ios:binary); car1.Bdatatofile(ddatafile); motor1.Bdatatofile(ddatafile); cout对象对象car1:end
69、l; coutcar1; cout对象对象motor1:endl; coutmotor1; cout对象对象car2:endl; coutcar2; cout对象对象motor2:endl; coutmotor2; ddatafile.close();9.4.3 二进制文件的读写二进制文件的读写 ifstream sdatafile(d:Ex9_10.data,ios:in|ios:binary); /重新打开文件重新打开文件,从头读取数据从头读取数据 car2.Bdatafromfile(sdatafile);/从文件读取数据复制到对象从文件读取数据复制到对象car2 if(sdatafil
70、e.eof()=0) cout读文件成功读文件成功endl; cout对象对象car2:endl; coutcar2; motor2.Bdatafromfile(sdatafile); /继续从文件读取数据复制到对象继续从文件读取数据复制到对象motor2 if(sdatafile.eof()=0) cout读文件成功读文件成功endl; cout对象对象motor2:endl; coutmotor2; sdatafile.close(); return 0;9.4.4 文件的随机访问(选读)文件的随机访问(选读)【例例9.11】使使用用随随机机访访问问对对【例例9.10】进进行行改改造造。将
71、将入入口口(main())程程序序中中的的文文件件改改为为输输入入输输出出文文件件,写写完完后后将将文文件件定定位指针拨回文件开始处。位指针拨回文件开始处。对应商品类中两成员函数,参数类型改为对应商品类中两成员函数,参数类型改为 fsream& : Bdatatofile(fstream&dest); Bdatafromfile(fstream&dest);主函数相关部分如下:主函数相关部分如下: fstream datafile(d:Ex9_11.data, ios:in|ios:out|ios:binary); /打开打开输入输出文件输入输出文件 car1.Bdatatofile(data
72、file); /保存对象保存对象 motor1.Bdatatofile(datafile); datafile.seekg(50,ios:beg); /一个记录一个记录50字节字节 motor2.Bdatafromfile(datafile); /先重写先重写motor2 datafile.seekg(ios:beg); /指针回到开始指针回到开始 car2.Bdatafromfile(datafile); /后重写后重写car29.5 字符串流(选读)字符串流(选读)【例例9.12】int main() int i; char str36=This is a book.; char ch;
73、istrstream input(str,36); /以串流为信息源以串流为信息源 ostrstream output(str,36); cout字符串长度:字符串长度:strlen(str)endl; for(i=0;ich; /从输入设备从输入设备(串串)读入一个字符读入一个字符,所有空白字符全跳过所有空白字符全跳过 coutch; /输出字符输出字符 coutendl;9.5 字符串流(选读)字符串流(选读) int inum1=93,inum2; double fnum1=89.5,fnum2; outputinum1 fnum10; /加空格分隔数字加空格分隔数字 cout字符串长度
74、:字符串长度:strlen(str)inum2fnum2; cout整数:整数:inum2 t 浮点数:浮点数:fnum2endl; cout字符串长度:字符串长度:strlen(str)endl; return 0;例例9.13 9.13 面向对象的面向对象的C+C+程序设计的固定框架程序设计的固定框架 class inventory string Description; /商品名称商品名称 string No; /货号货号 int Quantity; /数量数量 double Cost; /价格价格 double Retail; /零售零售public: inventory(string
75、 =#,string=#,int =0,double =0,double =0); friend ostream&operator(istream&sour,inventory&iv); bool operator=(inventory &); /货号为关键字货号为关键字 bool operator=(inventory &);inventory:inventory(string des,string no,int quan, double cost,double ret) Description=des; No=no; Quantity=quan; Cost=cost; Retail=ret
76、;ostream &operator(ostream&dest,inventory&iv) destleftsetw(20)iv.Descriptionsetw(10)iv.No; destrightsetw(10)iv.Quantitysetw(10)iv.Cost setw(10)iv.Retail(istream&sour,inventory&iv) if(sour=cin) cout请输入货物名称:请输入货物名称:iv.Description; cout请输入货号:请输入货号:iv.No; cout请输入货物数量:请输入货物数量:iv.Quantity; cout请输入货物价格:请输入
77、货物价格:iv.Cost; cout请输入货物零售价格:请输入货物零售价格:iv.Retail; else souriv.Descriptioniv.Noiv.Quantityiv.Costiv.Retail; return sour;bool inventory:operator=(inventory & inven) return No=inven.No;bool inventory:operator=(inventory & inven) return No=inven.No;template class Array T *elements; int Subscript; /已用最大下标
78、值已用最大下标值 int maxSize; fstream datafile;public: Array(int=2); /为了便于检验为了便于检验,默认元素数暂为默认元素数暂为2 Array(); bool IsFull() constreturn Subscript=maxSize-1; void renews(); /内存扩大一倍内存扩大一倍 void show() cout已用最大下标值已用最大下标值Subscriptt 可用元素数可用元素数maxSizeendl; void ordinsert(T&); /输入时以货号为关键字排序输入时以货号为关键字排序 friend ostream
79、&operator(ostream&dest,Array&ar); ;template Array:Array(int maxs) maxSize=maxs; Subscript=-1; /私有数据不容许直接赋初值私有数据不容许直接赋初值,必须在构造函数中赋初值必须在构造函数中赋初值 T temp; elements=new TmaxSize; datafile.open(mydatafile.txt,ios:in); /如文件不存在,打开失败如文件不存在,打开失败 if(!datafile=0) while(!datafile.eof()datafiletemp;if(datafile.eo
80、f()=0) /读到无数据可读后读到无数据可读后,即读入不成功即读入不成功,eofbit为为1 ordinsert(temp); /即使原文件未排序,退出时按排好序的重存即使原文件未排序,退出时按排好序的重存 datafile.close(); /必须放此处,打开成功才能关闭必须放此处,打开成功才能关闭 datafile.clear(0); /曾经读到文件结束或文件打开失败,流无法恢复曾经读到文件结束或文件打开失败,流无法恢复template Array:Array() int i; datafile.open(mydatafile.txt,ios:out); for(i=0;i=Subscr
81、ipt;i+) datafileelementsi; datafile.close(); deleteelements;template void Array:renews() int i; T *temp=elements; maxSize*=2; elements=new TmaxSize; for(i=0;i=Subscript;i+) elementsi=tempi; deletetemp;template void Array:ordinsert(T & elem) int i,j; /输入时以货号为关键字排序输入时以货号为关键字排序 if(IsFull() renews(); fo
82、r(i=0;i=Subscript;i+) if(elem=i;j-) elementsj+1=elementsj; Subscript+; elementsi=elem; show();template ostream&operator(ostream&dest,Array&ar) int i; for(i=0;i=ar.Subscript;i+) coutar.elementsi; return dest;int main() Array mylist; inventory temp; char ch; cout是否输入新商品?是否输入新商品?Y or Nch; while(ch=Y|ch=y) cin.get(); /吸收回车吸收回车 cintemp; mylist.ordinsert(temp); cout是否继续输入?是否继续输入?Y or Nch; coutmylist; return 0;