1、第八章 C+流,8.1 C+流的概念 8.2 输入输出格式控制 8.3 文件操作 8.4 字符串流,8.1 C+流的概念,1、C+流:是指信息从外部输入设备(如键盘和磁盘)向计算机内部(即内存)输入和从内存向外部输出设备(如显示器和磁盘)输出的过程。这种输入输出过程被形象地比喻为“流”。,2、在C+语言中,数据的输入和输出(I/O)包括三个方面:,(1)标准I/O:对标准输入设备键盘和标准输出设备显示器的输入输出。,(2)文件I/O:对在外存磁盘上文件的输入输出。,3、C+语言系统为实现数据的输入和输出定义了一个庞大的类库,称为I/O类库。其所有类被包含在三个系统头文件中:,(3)串I/O:对
2、内存中指定的字符串存储空间的输入输出。,iostream.h:fstream.h:strstrea.h:,包含的类有:ios,iostream,istream,ostream,iostream _withassign, istream_withassign,ostream_withassign等。,包含的类有:fstream,ifstream,ofstream,fstreambase, 以及iostream.h 中的所有类。,包含的类有:strstream,istrstream,ostrstream, strstreambase,以及iostream.h 中的所有类。,在一个程序或一个编译单元
3、(即一个程序文件)中,当需要进行标准 I/O 时,则必须包含头文件 iostream.h,当需要进行文件 I/O 时,则必须包含头文件 fstream.h,当需要进行串 I/O 时,则必须包含头文件 strstrea.h。,C+还为用户进行标准 I/O 操作定义了四个类对象,分别是:,cin: cout: cerr: clog:,代表标准输入设备键盘,是 istream_withassign 流类的对象。,都是 ostream_withassign 流类的对象。,代表标准输出设备显示器,代表错误信息输出设备显示器,根基类,8.2 输入输出格式控制,8.2.1 ios类中的枚举常量,在根基类 i
4、os 中定义有三个用户需要使用的枚举类型,由于它们是在公用成员部分定义的,所以其中的每个枚举类型常量在加上 ios: 前缀后,都可以为本类成员函数和所有外部函数访问。,一、无名枚举类型,该枚举类型中定义的每个枚举常量都是用于设置控制输入输出格式的标志使用的。类型定义如下:,enum skipws,left,right,internal,dec,oct,hex,showbase,showpoint,uppercase,showpos,scientific,fixed,unitbuf,stdio ;,各枚举常量的含义如下:,(1)skipws:设置该标志后,从流中输入数据时跳过当前位置及后面的所有
5、连续的空白字符,从第一个非空白字符起读数,否则不跳过空白字符。默认为设置。,空格、制表符t、回车符r、换行符n统称为空白符。,(2) left、right、internal:用于设置输出数据的对齐格式。,left:在指定的域宽内按左对齐输出。,(3)dec、oct、hex:,right:在指定的域宽内按右对齐输出。这是默认设置。,internal:在指定的域宽内使数值的符号按左对齐、数值本身按右对齐输出。,域宽内剩余的字符位置用填充字符填充。,设置 dec 标志后,使以后的数值按十进制输出。,设置 oct 标志后,使以后的数值按八进制输出。,设置 hex 标志后,使以后的数值按十六进制输出。,
6、默认设置,(4)showbase:用于设置输出数值前面是否加上 “基指示符”。,八进制数的基指示符为数字0;十六进制数的基指示符为0x,十进制数没有基指示符。默认为不设置。,(5)showpoint:强制输出的浮点数中带有小数点和小数尾部的无效数字0。默认为不设置。,(6)uppercase:设置该标志后,使输出的十六进制数和浮点数中使用的字母为大写。否则为小写。默认为不设置。,(7)showpos:设置该标志后,使输出的正数前带有正号“+”。默认为不设置。,(8)scientific,fixed:用于设置浮点数的输出格式。,scientific:设置后使浮点数按科学表示法输出;,fixed:
7、设置后使浮点数按定点表示法输出;,默认时由系统根据输出的数值选用合适的表示法输出。,二、open_mode 枚举类型,该枚举类型中定义的每个枚举常量规定一种文件打开的方式,在定义文件流对象和打开文件时使用。类型定义如下:,enum open_mode in,out,ate,app,trunc,nocreate,noreplace,binary ;,三、seek_dir 枚举类型,该枚举类型中定义的每个枚举常量用于对文件指针的定位操作。类型定义如下:,enum seek_dir beg,cur,end ;,8.2.2 ios类中的成员函数,ios 类提供成员函数对流的状态进行检测和进行输入输出
8、格式控制等操作,每个成员函数的声明格式和简要说明如下:,int bad( );,int eof( );,int fail( );,void clear( );,/ 操作出错时返回非 0 值。,/ 读取到流中最后的文件结束符时返回非 0 值。,/ 操作失败时返回非 0 值。,/ 清除 bad,eof 和 fail 所对应的标志状态, / 使之恢复为正常状态值 0 ,使 good 标志 / 状态恢复为 1。,int good( );,/ 操作正常时返回非 0 值,当操作出错、失败 / 和读到文件结束符时均为不正常,则返回 0 。,char fill( );,char fill( char c );
9、,/ 返回当前用于 I/O 控制的格式状态字。,long flags( long f );,int precision( );,long flags( );,int precision( int n );,/ 设置浮点数的输出精度为 n,返回此/ 前的输出精度。系统预设置的输出精/ 度为 6,即输出的浮点数最多具有 6 / 位有效数字。,/ 返回当前使用的填充字符。,/ 重新设置流中的填充字符为 c 的值,/ 返回此前的填充字符。系统预设置/ 的填充字符为空格。,/ 重新设置格式状态字为 f 的值, / 返回此前的格式状态字。,/ 返回浮点数的输出精度(有效数字的位数)。,int rdstat
10、e( );,long unsetf( long f );,int width( );,long setf( long f );,int width( int w );,/ 设置下一个数据值的输出域宽为 w, / 返回上一个数据值的输出域宽,若无 / 规定则返回 0。 / 注意:此设置只对下一个输出数据有效。,/ 操作正常时返回 0 ,否则返回非 0 值,,/ 根据参数 f 设置相应的格式化标志,返/ 回此前的设置。参数 f 所对应的实参为 / 无名枚举类型中的枚举常量,可以同时/ 使用一个或多个常量,每两个常量之间/ 要用按位或操作符连接。,/ 根据参数 f 清除相应的格式化标志, / 返回此
11、前的设置。,/ 返回当前的输出域宽。输出域宽是指 / 输出的值在流中所占有的字节数。,因为所有 I/O 流类都是 ios 的派生类,所以它们的对象 都可以调用 ios 类中的成员函数和使用 ios 类中的格式化常量 进行输入输出格式控制。,下面以标准输出流对象 cout 为例说明输出的格式化控制。,例8-1:使用 dec、oct、hex 、showbase、uppercase 。,例8-2:设置输出域宽、对齐方式和填充字符等。,例8-3:浮点数的输出控制。,8.2.3 格式控制操纵符,数据输入输出的格式控制还有更简便的形式,就是使用系统头文件 iomanip.h 中提供的操纵符。使用这些操纵符
12、不需要调用成员函数,只要把它们作为插入操作符)的输出对象即可。这些操纵符及其功能如下:,dec,oct,ws,/ 输出换行符n并刷新流。刷新流:是指把流缓 / 冲区中的内容立即写入到对应的物理设备上。,endl,/ 转换为按十进制输出整数,这是系统预置的进制,/ 转换为按八进制输出整数,/ 从输入流中读取空白字符。,hex,/ 转换为按十六进制输出整数,ends,flush,/ 设置 f 所对应的格式化标志。功能与 / setf( long f )成员函数相同。,resetiosflages( long f ),setfill( int c ),setiosflages( long f ),s
13、etprecision( int n );,/ 设置浮点数的输出精度为 n。,/ 输出一个空字符0。,/ 只刷新一个输出流。,/ 清除 f 所对应的格式化标志,功能与 / unsetf( long f )成员函数相同。,/ 设置填充字符为 ASCII 码为 c 的字符。,setw( int w ),/ 设置下一个数据的输出域宽为 w。,在上面的操纵符中, dec、oct、hex 、 endl、 ends、 flush 和 ws 除了在 iomanip.h 中有定义外,在 iostream.h 中也有定义。 所以当程序或编译单元中只需要使用这些不带参数的操纵符时, 可以只包含 iostream.
14、h 文件,而不需要包含 iomanip.h 文件。,下面以标准输出流对象 cout 为例,说明使用操纵符进行的输出格式化控制。,例8-4:使用操纵符 dec、oct、hex 、setiosflags() 。,例8-5:使用操纵符设置输出域宽、对齐方式和填充字符等。,例8-6:使用操纵符控制浮点数的输出。,8.3 文件操作,1、所谓“文件”:一般指存储在外部介质(磁盘、磁带、光盘等)上数据的集合。,3、在磁盘上保存的信息是按文件的形式组织的,每个文件都对应一个文件名,并且属于某个物理盘或逻辑盘的目录层次结构中一个确定的目录之下。,2、数据的输入和输出除了可以在键盘和显示器上进行外,还可 以在磁盘
15、上进行。,8.3.1 文件的概念,磁盘是外部存储器,能够永久保存信息,并能够被重新读写和携 带使用。所以若用户需要把信息保存起来,以便下次使用,则必须把 它存储到外存磁盘上。,一个文件名由文件主名和扩展名两部分组成:,4、在C+程序中使用的保存数据的文件按存储格式分为两种类型,即:字符格式文件(字符文件)和内部格式文件(字节文件)。,(1)字符文件又称为ASCII码文件、文本文件。,(2)字节文件又称为二进制文件。,文件主名:是由用户命名的一个有效的C+标识符,一般不超过8个字符。,文件扩展名:是由用户命名的、13个字符组成的、有效的C+标识符。通常用来区分文件的类型。,在字符文件中,每个字节
16、单元的内容为字符的ASCII码,被读出后能够 直接送到显示器或打印机上显示或打印出对应的字符,供人们直接阅读。,在字节文件中,文件内容是数据的内部表示,是从内存中直接复制过来的。,对于字符信息,数据的内部表示就是ASCLL码表示,所 以在字符文件和在字节文件中保存的字符信息没有差别。,对于数值信息,数据的内部表示和ASCLL码表示截然不同,所以在字符文件和在字节文件中保存的数值信息也截然不同。,当建立的文件主要是为了进行数据处理时,则适宜建立成字节文件,以节省存储空间和提高输入输出速度。,当建立的文件主要是为了输出到显示器或打印机供人们阅读,或者是为了供其他软件使用时,则适宜建立成字符文件。,
17、内部表示(二进制形式),ASCLL码表示,如:短整型数 1069,5、C+系统把各种外部设备也看作为相应的文件。如把标准输入设备键盘和标准输出设备显示器看作为标准输入输出文件,其文件名(又称设备名)为 con。,6、无论是字符文件还是字节文件,在访问它之前都要定义一个文件流类的对象,并用该对象打开它,以后对该对象的访问操作就是对被它打开文件的访问操作。对文件操作结束后,再用该对象关闭它。,对文件的访问操作包括输入和输出两种:,输入操作:是指从外部文件向内存变量输入数据。用于输入操作的文件称为输入文件。,输出操作:是指把内存变量或表达式的值写入到外部文件中。用于输出操作的文件称为输出文件。,7、
18、一个文件中保存的内容是按字节从数值 0 开始顺序编址的,文件开始位置的字节地址为 0,文件内容的最后一个字节的地址为 n-1(假定文件长度为 n),文件最后存放的文件结束符的地址为 n。,8、对于每个打开的文件,都存在着一个文件指针,初始指向一个隐含的位置,该位置由具体打开方式决定。,9、在程序中使用文件时,首先要在开始包含# include 命令。然后定义所需要的文件流对象,并用该对象调用 open 成员函数打开一个文件。,open 成员函数的声明格式为:,void open(const char * fname,int mode);,其中:,fname :用于指向要打开文件的文件名字符串,
19、该字符串内可以带有盘符和路径名,若省略盘符和路径名则隐含为当前盘和当前路径。,mode :用于指定文件的打开方式,对应的实参是 ios 类中定义的 open_mode 枚举类型中的枚举常量,或由这些枚举常量构成的按位或表达式。,open_mode 枚举类型中的每个枚举常量的含义如下:,ios:in / 使文件只用于数据输入,即从中读取数据。,ios:out / 使文件只用于数据输出,即向它写入数据。,ios:ate / 使文件指针移至文件尾,即最后位置。,ios:app / 使文件指针移至文件尾,并只允许向文件尾/ 输出(即追加)数据。,ios:trunc / 若打开的文件存在,则清除其全部内
20、容,/ 使之变为空文件。,ios:nocreate / 若打开的文件不存在则不建立它,/ 返回打开失败信息。,ios:noreplace / 若打开的文件存在,则返回打开失败信息。,ios:binary / 规定打开的为二进制文件,/ 否则打开的为字符文件。,10、下面对文件的打开方式作几点说明:,(1)文件的打开方式可以为上述的一个枚举常量,也可以为多个枚举常量构成的按位或表达式。例如:,ios:in | ios:out,/ 规定打开的文件同时用于输入和输出。,ios:app | ios:nocreate,/ 规定只向打开的文件尾追加数据, / 若文件不存在则返回打开失败信息。,(2)使用
21、open 成员函数打开一个文件时,若由字符指针参数所指定的文件不存在,则就建立该文件(空文件)。但若打开方式中含有 ios:nocreate 选项,则不建立新文件,并且返回打开失败信息。,(3)当打开方式中不含有 ios:ate 和 ios:app 选项时,则文件指针被自动移到文件的开始位置。当打开方式中含有 ios:out 选项,但不含有 ios:in, ios:ate 或 ios:app选项时,若打开的文件存在,则原有内容被清除,使之变为一个空文件。,(4)当用输入文件流对象调用 open 函数打开一个文件时,打开方式参数可以省略,默认按 ios:in 方式打开。当用输出文件流对象调用 o
22、pen 函数打开一个文件时,打开方式参数也可以省略,默认按 ios:out 方式打开。,下面给出定义文件流对象和打开文件的例子:, ofstream fout;fout.open( a:xxk.dat );, fstream fio;fio.open( a:abc.ran, ios:in | ios:out | ios:binary );,11、在每一种文件流类中,还定义有带参构造函数,其所带参数与 open 成员函数的参数完全相同,所以当定义带有实参表的文件流对象时,其功能与先定义不带参数的文件流对象,后调用 open 成员函数打开文件的功能完全相同。,例如:, ofstream fout(
23、 a:xxk.dat );, ifstream fin; fin.open( a:wr.dat , ios:in | ios:nocreate );, ifstream fin( a:wr.dat , ios:in | ios:nocreate );, fstream fio( a:abc.ran, ios:in | ios:out | ios:binary );,12、每个文件流类中都提供一个关闭文件的成员函数 close ( ),当打开的文件操作结束后,就需要关闭它,使文件流与对应的物理文件断开联系,并能够保证最后输出到文件缓冲区中的内容,无论是否已满,都将立即写入到对应的物理文件中。,8
24、.3.2 字符文件的访问操作,当只需要对数据进行顺序输入输出操作时,适合使用字符文件。,对字符文件的访问操作包括向字符文件顺序输出数据和 从字符文件顺序输入数据两个方面。,顺序输出:就是依次把数据写入到文件的末尾。,顺序输入:就是从文件开始位置起依次向后提取数据,直到遇到文件结束符为止。,1、向字符文件输出数据,向字符文件输出数据有两种方法:,(1)调用从 ostream 流类中继承来的插入操作符重载函数。,声明格式:ostream ,采用这种方法时,插入操作符左边是文件流对象,右边是要输出到该文件流(即对应的文件)中的数据项。,若要向字符文件中插入一个用户定义类型的数据,除了可 以将每个域的
25、值依次插入外,还可以进行整体插入,但必须预 先对该类型数据定义有插入操作符重载函数。,(2)调用从 ostream 流类中继承来的 put 成员函数。,声明格式: ostream & put( char );,采用这种方法时,文件流对象通过点操作符、文件流指针通过箭头操作符调用成员函数 put。,例8-7:向 a 盘上的 wr1.dat 文件输出 020 之间的整数。,例8-8:把从键盘上输入的若干行文本字符存人到 a 盘上的 wr2.dat 文件中,直到按下 Ctrl+z 组合键为止。,2、从字符文件输入数据,从打开的字符文件中输入数据到内存变量有三种方法:,(1)调用从 istream 流
26、类中继承来的提取操作符重载函数。,声明格式: istream ,每次从文件流中提取用空白符隔开的一个数据,在读取 一个数据前文件指针自动跳过空白字符,向后移到非空白字 符时读取一个数据。,(2)调用从 istream 流类中继承来的 get 成员函数。,声明格式: int get( );istream & get( char & );,每次从文件流中提取一个字符(不跳过任何字符)并作为返回值返回.,每次从文件流中提取一个字符到引用变量中,不跳过任何字符.,(3)调用从 istream 流类中继承来的 getline 成员函数。,声明格式:istream & getline( char * bu
27、ffer, int len, char=n );,每次从文件流中提取以换行符隔开(最后一行数据以文 件结束符为结束标志)的一行字符到字符指针 buffer 所指向 的存储空间中,若碰到换行符之前所提取字符的个数大于等 于参数 len 的值,则本次只提取 len-1 个字符,被提取的一行 字符是作为字符串写入到 buffer 所指向的存储空间中的。,当使用流对象调用 get( ) 成员函数时,通过判断返回值是否等于文件结束符 EOF 可知文件中的数据是否被输入完毕。,当使用流对象调用其他三个成员函数时,若提取成功则 返回非 0 值,若提取失败(即已经读到文件结束符,未读到 文件内容)则返回 0
28、值。,若一个文件是使用插入操作符输出数据而建立的,则当作输入文件打开后,应使用提取操作符输入数据;,若一个文件是使用 put 成员函数输出字符而建立的,则当 作输入文件打开后,应使用 get( ) 或 get( char&) 成员函数输入字符数据;,若每次需要从一个输入文件中读入一行字符时,则需要使用 getline 成员函数。,例8-9:从例8-7 所建立的 a:wr1.dat 文件中输入全部数据并依次显示到屏幕上。,在通常情况下:,例8-10:从例8-8 所建立的 a:wr2.dat 文件中按字符输入全部数据,把它们依次显示到屏幕上,并且统计出文件内容中的行数。,8.3.3 字节文件的访问
29、操作,字节文件:是指在打开方式中带有 ios:binary 选项的文件。,字节文件可以是输入文件、输出文件、既输入又输出的文件。,向字节文件中输出信息:就是把内存中由指定字符指针所指向的具有一定字节数的内容原原本本地写入到文件中。,从字节文件中输入信息:就是把具有一定字节数的内容原原本本地拷贝到内存中由指定字符指针所指向的存储空间中。,一个文件被用户定义的一个文件流对象打开后,通过文件流对象调用在 istream 流类中定义的 read成员函数能够从文件中读出信息。,通过文件流对象调用在 ostream 流类中定义的 write 成员函数能够向文件中写入信息。,这两个成员函数的声明格式如下:,
30、istream & read( char * buffer, int len ); ostream & write( const char * buffer, int len );,其中:,字符指针 buffer :用于存放内存中保存文件读写信息的一 块存储空间的首地址。,len:用于存放一次读写文件的字节数。,当调用 read 成员函数时若读到了 len 个字节内容,则返 回非 0 值,若读到了文件结束符,则返回 0 值,此时通过调 用 istream 流类中提供的 gcount( ) 成员函数能够返回实际读 取的字节数。,文件指针的移动:,输入文件:利用 istream 流类中提供的 se
31、ekg 成员函数能够把文件指针移动到指定的位置。,输出文件:利用 ostream 流类中提供的 seekp 成员函数能够把文件指针移动到指定的位置。,istream & seekg( long dis, seek_dir ref=ios:beg ); ostream & seekp( long dis, seek_dir ref=ios:beg );,seek_dir:是在 ios 根基类中定义的枚举类型,它包含三个常量:,这两个成员函数的声明格式如下:,其中:,ios:beg: ios:cur: ios:end:,文件开始位置(字节地址为 0 的位置)。,当前文件指针位置。,文件结尾位置(即
32、文件结束符位置)。,ref :用于指定移动文件指针的参考点,取上述三个常量值之一,默认值为 ios:beg 。,dis :文件指针移动的距离(字节数)。,在 istream 流类和 ostream 流类中还分别定义有两个成员函数:,当 ref 被指定为 ios:beg 时,dis 不能小于 0; 当 ref 被指定为 ios:end 时,dis 不能大于 0;,例8-11:首先用 10 个整数初始化一个整型数组,然后把数组中每个元素的值依次写入到字节文件 a:shf1.dat 中。,例8-12:求出 a:shf1.dat 文件中保存的所有整数的最大值、最小值和平均值。,当 dis 为正时,表示
33、后移;当 dis 为负时,表示前移。,tellg( ) :用来返回输入文件中文件指针的位置。tellp( ) :用来返回输出文件中文件指针的位置。,8.4 字符串流,1、字符串流类包括三种:,输入字符串流类:istrstream,输出字符串流类:ostrstream,输入输出字符串流类:strstream,2、字符串流对应的访问空间是内存中由用户定义的字符数组。,3、由于字符串流和文件流都是输入流类 istream 和输出流类ostream 的派生类,所以对它们的操作方法基本相同。唯一的区别是:,每个文件都有文件结束符标志,利用它可以判断读取数据是否到达文件尾。,字符串流所对应的字符数组中没有
34、相应的结束符标志,只能靠用户规定一个特殊字符作为其结束符使用。,它们都被定义 在系统头文件 strstrea.h 中。,4、每一种字符串流类都不带有 open 成员函数,所以只有在定义字符串流的同时给出必要的参数,通过自动调用相应的构造函数来使之与一个字符数组发生联系。,三种字符串流类的构造函数的声明格式如下:,(1)istrstream( const char * buffer );,(2)ostrstream( char * buffer, int n );,(3)strstream( char * buffer, int n, int mode );,该构造函数建立输入字符串流,对应的字
35、符数组空间由 buffer 指针所指向。,该构造函数建立输出字符串流,对应的字符数组空间由 buffer 指针所指向,该空间的大小(即字节数)由第二个参数 n 给出。,该构造函数建立输入输出字符串流,,第一个参数:指定对应的字符数组存储空间, 第二个参数:指定空间的大小, 第三个参数:指定打开方式。,定义了一个输入输出字符串流 strio,使用的字符数组 为 a3,大小为 a3 数组的长度,打开方式规定为既能够用于 输入又能够用于输出,进行输入的数据来自数组 a3,进行 输出的数据写入数组 a3。,5、下面给出定义相应字符串流的例子:,(1)ostrstream strout( a1, 50
36、);,定义了一个输出字符串流 strout,使用的字符数组为 a1, 大小为 50 个字节,以后对 strout 的输出都将被写入到字符 数组 a1 中。,(2) istrstream strin( a2 );,定义了一个输入字符串流 strin,使用的字符数组为 a2, 以后从 strin 中读取的输入数据都将来自字符数组 a2 中。,(3)strstream strio( a3, sizeof( a3 ), ios:in | ios:out );,6、一个字符串流被定义后就可以调用相应的成员函数进行数据的输入、输出操作,就如同使用文件流调用相应的成员函数进行有关操作一样。,7、对字符串流的
37、操作方法通常与字符文件流的操作方法相同。,例8-13:从一个字符串中得到每一个整数,并把它们依次存入到一个字符串流中,最后向屏幕输出这个字符串流。,习题八,一、 单选题,1、当使用 ifstream 流类定义一个流对象并打开一个磁盘文件时,文件的隐含打开方式为:_,A. ios:in ; B. ios:out ; C. ios:in | ios:out ; D. ios:binary ;,2、当使用 ofstream 流类定义一个流对象并打开一个磁盘文件时,文件的隐含打开方式为:_,A. ios:in ; B. ios:out ; C. ios:in | ios:out ; D. ios:bi
38、nary ;,3、当需要使用 istrstream 流类定义一个流对象并联系一个字符串时,应在文件开始使用 # include 命令,使之包含_文件.,A,B,A. iostream.h B. iomanip.h C. fstream.h D. strstrea.h,D,二、 填空题,1、在 C+ 流类库中,根基类为 _。,2、在 C+ 流类库中,输入流类和输出流类的名称分别为_和_。,3、若要在程序文件中进行文件输入输出操作,则必须在开始的 # include 命令中使用_头文件。,4、当从字符文件中读取回车和换行两个字符时,被系统看作为一个_。,ios,istream,fstream.h,换行符,ostream,