Download presentation
Presentation is loading. Please wait.
Published byΣoφпїЅα Λύκος Modified 5年之前
1
第8章 I/O流 8.1 C++流的概念 8.2 C++的输入输出 8.3 文件流与文件操作 8.4 重载插入运算符和提取运算符
8.3 文件流与文件操作 8.4 重载插入运算符和提取运算符 8.5 编程实例
2
8.1 C++流的概念 C++流是指信息在内存与外部设备之间传递(流动)的过程。 C++流程及以下概念:
l 标准I/O流:内存与标准输入输出设备之间信息的传递; l 文件I/O流:内存与外部文件之间信息的传递; l 字符串I/O流:内存变量与表示字符串流的字符数组之间信息的传递。
3
每一种I/O流都分为输入、输出和既输入又输出三种流动方式。各流动方式的流动方向如下:
l 输入:从外部向内存变量流动; l 输出:从内存变量向外部流动; l 既输入又输出:内存和外部之间允许双向流动。 在C++流类库中定义有各种流类供用户直接使用,它们分别包含在不同的头文件中:
4
l iostream.h包含ios, istream, ostream, iostream等;
l fstream.h包含ifstream, ofstream, fstream及iostream.h中的所有内容; l strstrea.h包含istrstream, ostrstream, strstream及iostream.h中的所有内容。 流类库员列表如表8.1所示。
5
表8.1 流类库成员列表 流类分类 流类名称 流 类 作 用 流基础类 ios 所有流类的父类,保存流的状态并处理错误 输入流类
表8.1 流类库成员列表 流类分类 流类名称 流 类 作 用 流基础类 ios 所有流类的父类,保存流的状态并处理错误 输入流类 istream 输入流基础类,将流缓冲区中的数据作格式化和非格式化之间的转换并输入 ifstream 文件输入流类 istream_withassign cin输入流类,即操作符>>输入流 istrstream 串输入流类
6
输出流类 ostream 输出流基础类,将流缓冲区中的数据作格式化和非格式化之间的转换。并输出 ofstream 文件输出流类 ostream_withassign Cout、cerr、clog的输出流类,即操作符<<输出流 ostrstream 串输出流类 文件流类 fstreambase 文件流基础类 串流 strstreambase 串流基础类 输入/输出流类 iostream 多目的输入/输出流类的基础类 fstream 文件流输入/输出类 strstream 串流输入/输出类
7
在iostream.h 头文件中定义有cin、cout和cerr等流类对象,分别表示标准输入设备键盘、标准输出设备显示器和保存错误信息的设备显示器。标准设备见表8.2。
表8.2 标准设备数 C++名字 设备 C中的名字 默认的含义 cin cout cerr clog 键盘 屏幕 打印机 stdin stdout stderr stdprn 标准输入 标准输出 标准错误
8
8.2 C++的输入输出 输入/输出流可以从文件或设备中读出或写入数据,C++运行库提供了两种输入/输出功能:
(1) 把数据作为单字符流处理。可以处理简单的数据,也可以处理复杂的数据结构。 (2) 直接调用操作系统的底层功能实现输入/输出操作。 这两种输入/输出功能都含文件和标准的输入/输出功能。这里着重讨论流类的标准输入/输出、标准错误输出以及文件输入/输出。
9
标准输入输出 C++用istream_withassign类和ostream_withassign类来实现标准输入/输出功能。标准输入与输出是指读键盘的数据和将数据输出到屏幕。 在iostream.h文件中用以下两条语句定义cin和cout两个标准对象: istream_withassign cin ; ostream_withassign cout ;
10
标准错误输出cerr 当程序测试并处理关键错误时,若不希望程序的错误信息从屏幕显示,而是重定向到其它地方,这时使用cerr流显示信息。 例8.1 在除法操作不能进行时显示一条错误信息。 #include <iostream.h> void fn(int a,int b) {
11
if(b==0) cerr << "zero encountered. " << "The message can not be redirected." ; else cout << a /b << endl ; } void main() { fn(20,2); fn(20,0);
12
运行结果为: 10 zero encountered . The message can not be redirected . 说明:主函数第一次调用fn()函数时,没有碰到除0运算,于是得到文件的写内容10,第二次调用fn()函数时,碰到了除0运算,于是在屏幕上输出错误信息。写到cerr上的信息是不能被重定向的,因此它只能在屏幕上显示。
13
输入/输出格式控制 在程序中有时需要关心输入/输出的格式,C++提供了两种格式控制函数:一种是ios类中定义的格式控制成员函数;另一种是基于流对象的操纵函数。相比之下,使用操纵函数更为方便。 1. ios类中定义的格式控制标志 ios类中定义了一个数据成员和一个格式控制标志字long x_flags。x_flags的每一位的状态值用枚举符号常量定义,其具体含义如下:
14
enum { skipws = 0x0001 , //跳过输入空格 left = 0x0002 , //按左对齐格式输出 right = 0x0004 , //按右对齐格式输出 internal = 0x0008 , //输出符号和基指示符后的填补 dec = 0x0010 , //转换为十进制(In / Out) oct = 0x0020 , //转换为八进制(In / Out) hex = 0x0040 , //转换为十六进制(In / Out) showbase = 0x0080 , //输出显示基指示符
15
showpoint = 0x0100 , //输出显示小数点
uppercase = 0x0200 , //大写十六进制输出 showpos = 0x0400 , //正整数显示前加上“+” scientific = 0x0800 , //输出用科学表示法表示的浮点数 fixed = 0x1000 , //输出用固定小数点表示的浮点数 unitbuf = 0x2000 , //在输出操作后刷新所有流 stdio = 0x //在输出操作后刷新stdout和stderr } 这些标志可以由ios类的成员函数flags()、setf()和unsetf()访问,也可以用操纵函数操作。
16
2. 操纵函数和操纵符 操纵函数分为带参数和不带参数两种。不带参数的操纵函数又叫操纵符,定义在头文件iostream.h中,带参数的操纵函数定义在头文件iomanip.h中。Fostream.h中的操纵符如表8.3所示。Jomanip.h中的操纵函数如表8.4所示。
17
表8.3 iostream.h中的操纵符 操纵符 用法举例 结 果 说 明 dec
cout<<dec<<intvar; 将整数转化为十进制格式输出 cin>>dec>>intvar; 将整数转化为十进制格式输入 hex cout<<hex<<intvar; 将整数转化为十六进制格式输出 cin>>hex>>intvar; 将整数转化为十六进制格式输入 oct cout<<oct<<intvar; 将整数转化为八进制格式输出 cin>>oct>>intvar; 将整数转化为八进制格式输入 ws cin>>ws 忽略输入流中的空格 end cout<<endl; 插入换行符,刷新流 ebds cout<<ends; 插入串最后的串结束符 flush cout<<flush; 刷新一个输入流
18
表8.4 iomanip.h中的操纵函数 操纵符 用 法 举 例 结 果 说 明 setprecision(int)
cout<< setprecision(6) 输出精度为6位小数的浮点数 cin>> setprecision(15) 输入精度为15位小数的浮点数 setw(int) cout<<setw(6)<<var; 输出宽度为6的数据 cin>>setw(24)>>buf; 输入宽度为24的数据 setiosflags(long) cout<<setioflags(ios::hex| ios::uppercase) 指定数据输出的格式为十六进制格式且用大写字母输出 cin>>setioflags(ios::oct| ios::skipws) 指定数据输入的格式为八进制格式且跳过输入中的空白 reseriosflags(long) cout<< reseriosflags(ios::dec) 取消数据输出的十进制格式 cin>> reseriosflags(ios::hex) 取消数据输入的十六进制格式
19
例8.2 操纵符(函数)的使用。 #include<iomanip.h> void main() { int x=56; cout<<setw(6)<<x; cout<<setw(6)<<oct<<x; cout<<setw(6)<<hex<<x<<endl; cout.setf(ios::left); cout<<setw(6)<<dec<<x;
20
cout<<setw(6)<<oct<<x;
cout<<setw(6)<<hex<<x<<endl; cout.unsetf(ios::left); cout<<setw(6)<<dec<<x; } 运行结果为:
21
8.3 文件流与文件操作 8.3.1 标准库文件函数输入/输出
8.3 文件流与文件操作 标准库文件函数输入/输出 标准C++兼容C语言的文件输入输出功能,它们是以函数的形式给出的。常用的文件输入/输出函数有fopen、fclose、fwrite和fread等。 标准的C库文件输入输出函数定义在stdio.h头文件中,并且定义了一个FILE型文件结构。
22
在每一个文件被打开时,都有一个FILE型文件指针与之关联,以便保存文件的相关信息,完成文件的读写操作。
对文件的操作一般分为3个步骤: l 使用文件打开函数打开文件,并与文件指针建立关系; l 利用文件指针和写文件、读文件函数对文件进行操作; l 使用文件关闭函数后再关闭文件。 1. 打开文件函数fopen 可使用fopen函数完成文件操作第一步,该函数定义为: FILE *fopen(const char * filename,const char * mode);
23
其中,参数filename是文件名字符串,mode为文件打开的模式。使用fopen函数时应注意:
(1) 返回值是返回打开文件的指针,如果文件不存在或者不能找到文件,则fopen调用返回空指针(NULL),表示文件打开失败。
24
(2) 参数mode可取以下值: l “r”:以只读方式打开文件; l “w”:以写方式打开文件,如果文件已经存在,其内容将被破坏; l “r+”:用读和写的方式打开文件(文件必须已经存在,否则将导致异常); l “w+”:用读和写的方式打开一空文件,如果该文件已经存在,其内容将被破坏; l “a+”:为了读文件和附加新内容而打开文件; l “t”:以以文本方式打开文件; l “b”:以二进制方式打开文件。
25
2. 文件读写函数fread和fwrite 第二步操作就是要对文件进行读和写数据。一般常用fread函数和fwrite函数完成该步操作,它们的使用格式为: size_t fread(void * buffer,size_t size, size_t count,FILE * stream); size_t fwrite(const void * buffer, size_t size, size_t count, FILE * stream); 使用fread和fwrite函数时应注意以下几点: (1) fread函数读文件后返回文件数据记录的数目。 (2) fwrite函数写文件后返回实际写入文件数据记录的数目。
26
(3) 参数buffer指向数据缓冲区。fread函数将读出的数据放在缓冲区中供程序使用;fwrite函数将待写入文件的数据放在缓冲区中以便写入文件。
(4) size为从文件中读出和写入文件的字节数。 (5) count为一次从文件读出数据的最大记录数和一次写入文件的最大记录数。 (6) stream为文件结构指针,在fread函数中为打开文件的指针;在fwrite函数中为把数据要写入文件的指针。
27
3. 关闭文件函数fclose 可以使用fclose完成第三步操作,该函数的使用格式为: int fclose(FILE * stream); //关闭流文件 int _fcloseall(void); //关闭所有流文件 使用fclose函数时应注意以下几点: (1) fclose函数返回值为0时表示文件关闭成功,否则返回值为非0。 (2) fcloseall成功关闭所打开的文件时,函数返回值为关闭文件的总数。 (3) fclose函数的参数为指向FILE结构体的指针。
28
例8.3 建立一个名为FREAD.OUT的文件并写入25个字符,然后从该文件中读出这25个字符,输出到屏幕上。
#include<stdio.h> void main() { FILE * stream; char list[30]; int i,numread,numwritten;
29
//按文本模式打开文件并写数据 if((stream = fopen("FREAD.OUT","W+T"))!=NULL) { for(i = 0;i < 25;i++) list[i] = (char)( 'z'- i); numwritten = fwrite(list ,sizeof(char),25,stream); printf("Write %d items\n",numwritten); fclose(stream); } else printf("problem opening the file\n");
30
//读出25个字符 if((stream = fopen("fread.out","r+t"))!=NULL); { numread = fread(list,sizeof(char),25,stream); printf("Number of items read = %d\n",numread); printf("Contents of buffer = %.25s\n",list); fclose(stream); } else printf("File could not be opened\n");
31
运行结果为: Write 25 items Number of items read = 25 Contents of buffer = zyxwvutsrqponmlkjihgfedcb
32
文件输入/输出流 在C++中,对文件的输入/输出操作提供了另一种操作方式,即流类库的文件操作,这些类是ofstream与ifstream文件输入/输出流类。对文件的操作也需要8.3.1节讲述的三个步骤。 1. 文件的输出 文件的输出由ofstream完成。ofstream由ostream类继承而来,并继承了它的操作,因此可以利用ofstream重载的操作符<<把数据写入文件流中。进行文件输出时首先要创建文件输出流ofstream类的一个对象。ofstream类的构造函数用于在不同情况下构造文件流。
33
ofstream类构造函数的定义如下: ofstream(); ofstream(const char * filename,int mode=ios::out,int prot=filebuf::openprot); 1) 文件的打开 第一个构造函数用于构造一个不带参数的流,如果需要可以再用它的open函数打开一个文件。open函数的定义如下: void open(const char * filename,int filemode = ios::out,int access = filebuf:: openprot);
34
open函数需要三个参数:第一个参数为字符串,即打开文件的文件名;第二个参数为文件模式,缺省为ios::out,表示按输出方式打开,其它文件操作模式见表8.3;第三个参数为文件的保护方式,有8种取值,其中,0表示普通文件,1表示只读文件,2表示隐含文件,4表示系统文件,以及它们的组合。例如,打开一个test1.dat文件可用如下语句: ofstream afile(); afile.open("test1.d 或 afile.open("test1.dat",ios::out|ios::binary);
35
表8.5 文件操作方式 操作模式 说 明 ios::in 使文件只用于数据输入 ios::out 使文件只用于数据输出 ios::app
表8.5 文件操作方式 操作模式 说 明 ios::in 使文件只用于数据输入 ios::out 使文件只用于数据输出 ios::app 使文件指针移至文件尾,并只允许向文件尾输出数据 ios::nocreate 若打开的文件不存在则返回打开失败信息 ios::noreplace 若打开的文件存在则返回打开失败信息 ios::binary 以二进制方式打开文件
36
第二个构造函数可以在创建文件时通过第一个参数直接指定文件名,第一、第三个参数同上述open函数参数。
可用如下语句打开一个test1.dat文件: ofstream afile("test1.dat"); ofstream afile("test1",ios::noreplace); ofstream afile("test1",ios::append); ofstream afile("test1",ios::out|ios::binary);
37
2) 文件的数据写入函数 ofstream类是从ostream类继承而来的,因而可以使用ostream类的写数据函数和操作符把数据写入文件。写数据函数的原型如下: //写入字符 inline ostream& put(char); ostream& put(unsigned char); inline ostream& put(signed char); //将字符串str的内容写入到文件
38
ostream& write(const char * ,int);
inline ostream& write(const unsigned char * ,int); inline ostream& write(const signed char * ,int); //移动输出文件的位置指针 ostream& seekp(streampos); ostream& seekp(streamoff,ios::seek_dir); //取当前输出文件位置指针的值 streampos tellp();
39
3) 文件的关闭函数 ofstream类也是从fstreambase类继承的,因而可以使用fstreambase类的close();来关闭文件。 2. 文件的输入 文件的输入由类ifstream完成,它由iostream和fstreambase类继承而来,并继承了fatreambase类的操作符>>函数以及文件打开、从文件中读数据和关闭文件的函数。ifstream也提供了两构造函数: 第一个为: ifstream();
40
第二个为: ifstream(const char * filename,int mode = ios::in,int prot = filebuf::openprot); 1) 文件的打开 第一个构造函数用于构造一个不带参数的流,如果需要可以在用它的open函数打开一个文件。open函数的定义如下: void open(const char * filename,int filemode=ios::in,int access=filebuf::openprot); 第一个构造函数使用与第二个构造函数相同的文件名打开输入文件。
41
2) 文件的数据读出函数 可以使用istream类的读数据的函数和操作符从文件中读取数据,下面是读数据函数的原型。 //将文件数据读取到字符串内 inline istream& get(char * ,int , char ='\n'); inline istream& get(unsigned char * ,int , char ='\n'); inline istream& get(signed char * ,int , char ='\n');
42
//将文件数据读取到字符内 istream& get(char &); inline istream& get(unsigned char &); inline istream& get(signed char &); istream& get(streambuf& , char='\n'); //将文件一行数据读取到字符串内 inline istream& getline(char * , int , char='\n'); inline istream& getline(unsigned char * , int , char='\n'); inline istream& getline(signed char * , int , char='\n'); inline istream& ignore( int = 1 , int = EOF);
43
//将文件数据读取到字符串内 istream & read(char * ,int); inline istream& read(unsigned char * ,int); inline istream& read(signed char * ,int , char ='\n'); int gcount() const{return x_gcount;} 3) 文件的关闭函数 ifstream类也是从fstreambase类继承的,可以使用fstreanmbase类的close();来关闭文件。
44
3. 字符文件操作举例 例8.4 将100以内的所有奇数存入字符文件"a:wr1.dat"中。 #include<iostream.h> #include<fstream.h> void main(void) { ofstream f1("a:wr1.dat"); //定义输出文件流,并打开相应文件 for(int i=1;i<100;i+=2) f1<<i<<" "; //向f1文件流输出i值 f1.close(); //关闭f1所对应的文件 }
45
例8.5 从字符文件"a:wr1.dat"中依次读出每个整数并按6个一行显示出来。
#include<iomanip.h> #include<fstream.h> void main(void) { ifstream f1("a:wr1.dat", ios::in | ios::nocreate); int x,i=1;
46
while(f1>>x) if(i++%6) cout<<x<<' '; else cout<<x<<endl; cout<<endl; f1.close(); }
47
运行结果为: 97 99
48
例8.6 使用istream和ostream类的文件操作函数把文件file.in的内容拷贝到文件file.out中。
#include<iostream.h> #include<fstream.h> #include<process.h> void main() { char ch; ifstream fin("file.in"); //创建一个输入流,并与输入文件file.in联系
49
ofstream fout(file.out);
if(!fin) { cerr<<"Can't open file file.in"; exit(-1); //如果源文件出错,显示出错信息并退出 }
50
if(!fout) { cerr<<"Can't open file file.out"; exit(-1); //如果目的文件出错,显示出错信息并退出 } while(fout&&fin.get(ch)) fout.put(ch);
51
8.4 重载插入运算符和提取运算符 使用传统的C语言中的printf和scanf等输入函数编写的程序,当输入输出的数据类型发生变化时,必须重写所有的输入输出代码。使用C++流类后,这个问题不复存在,C++可以利用流类对象cin和cout的成员函数重载任意类的输入/输出操作。
52
流的输入运算符“>>”又称提取运算符,流的输出运算符“<<”又称插入运算符,它们都可以重载。
下面是一个用户定义的日期类Date。为保持和简单类型的输入/输出同样的形式,需要重载操作符<<和>>。重载的操作符<<用于输出Date类的成员变量,重载的操作符>>用于输入Date类的成员变量。由于重载的函数需要访问Date类的私有成员和保护成员,因此应该把这些重载的函数定义为Date类的友元。
53
例8.7 在用户定义的Date数据类中对流类运算符<<和>>进行重载。
#include<iostream.h> class Date { int mo,da,int y; public: Date(int m,int d, int y){mo=m;da=d;yr=y;} friend ostream& operator << (ostream& os,Date& dt);
54
friend istream& operator >> (istream& is,Date& dt);
}; ostream& operator << (ostream& os,Date& dt) { os << dt.mo <<'/' << dt.da << '/' << dt.yr; return os; //返回流的引用 } istream& operator >> (istream& is,Date& dt) is >> dt.mo >> '/' >> dt.da >> '/' >> dt.yr;
55
return is; //返回流的引用 } void main() { int mo,da,yr; Date dt(7,1,1997); cout << dt << endl; cin >> mo >> da >> yr ; Date otherdt(mo,da,yr); cout << otherdt << endl;
56
从这个例子可以看出,只要定义好了某个类的输入/输出操作符重载,就能很容易地进行此类输入和输出操作,这正是C++的一个重要的优点。重载的操作符函数有以下几个特点应该注意:
(1) 重载的操作符函数有两个参数,第1个参数是出现在<<或>>左边的操作数,第2个参数是出现在<<或>>右边的操作数,如果重载的是输出操作符<<,第1个参数是对流ostream类的引用;如果重载的是输入操作符>>,第1个参数是对流istream类的引用,第2个参数是对要定义的输入/输出类的引用,当然,也可以是该类的指针。
57
(2) 运算符重载必须定义为类的友元,因为左操作数必须是流类对象而不是输入/输出类对象,不能使用隐式左操作数。
58
8.5 编 程 实 例 下面程序利用istream和ostream类的操作符对点数据进行文件操作,从键盘上输入两个点的坐标,把它保存到磁盘文件上,并从磁盘上读出数据,写到显示器上。 #include <stdio.h> #include <conio.h> #include <istream.h> #include <ostream.h>
59
#include <fstream.h>
#include <process.h> class Point { protected: int X; int Y; public: Point(int x,int y){X = x;Y = y;}
60
friend ostream& operator<<(ostream& os,Point& aPoint);
friend istream& operator>>(istream& is,Point& aPoint); }; ostream& operator<<(ostream& os,Point& aPoint) { os<<" ("<<aPoint.X<<"."<<aPoint.Y<<")"; return os; } istream& operator>>(istream& is,Point& aPoint) is>>" (">>aPoint.X>>".">>aPoint.Y>>")";
61
return is; } int main() { int x,y; ofstream writefile("crest.sav"); if(!writefile) cout<<"Can't open file"; exit(-1); cout<<"input the x,y with blank seperated: ";
62
cin>>x>>y;
Point Point1(x,y); cout<<"input x,y of another point"; Point Point2(x,y); writefile<<Point1; writefile<<Point2; writefile.close(); ifstream readfile("ctest.sav"); if(!readfile)
63
{ cout<<"Can not open file"; exit(-1); } readfile>>Point1>>Point2; cout<<Point1<<endl; cout<<Point2<<endl; return 0;
64
小 结 C++的I/O流完全不同于C的I/O系统,它操作更简捷,更容易理解,它使标准I/O流、文件流和串流的操作在概念上统一了起来。有了控制符,C++更具灵活性,由它所重载的插入运算符,完全融入了C++的类及其继承的体系。
Similar presentations