Download presentation
Presentation is loading. Please wait.
1
第11章 输入输出流类
2
人机交互设备主要指键盘、显示器等终端控制台设备和打印机。
外部设备可分为人机交互设备和存储设备两个大类。 人机交互设备主要指键盘、显示器等终端控制台设备和打印机。 存储设备指磁带、磁(软、硬)盘、光盘等以文件形式存储持久数据的设备。
3
早期程序设计语言中通常以文件方式来统一人机交互设备和存储设备的输入输出操作。即将人机交互设备当作一种特殊文件对待。
以文件方式统一管理外部设备可以屏蔽实际物理设备的差异,有利于整体上简化程序设计语言与外部设备交换信息的处理过程。
4
C++语言对输入输出的处理引入了“流”这个比文件抽象程度更高的概念。输入输出操作被看作数据在源设备对象与目标设备对象之间的流动。
5
11. 1 C++流库结构 标准ANSI C程序中的绝大部分工作是由ANSI C标准库中的printf,scanf完成的,I/O功能作为ANSI C标准库功能的一部分被提供。在C++中iostream库提供了printf、scanf功能的替代方法,输入输出功能的实现由一个iostream对象完成。
6
C++ I/O流类的结构(1) conbuf strstreambuf filebuf streambuf 图11.1 流缓冲区类的派生关系 1. 流缓冲区类 在C++I/O类库定义文件iostream.h中有一个streambuf类,它用来提供物理设备的接口。缓冲区由一段预留的字符存储空间和两个指针组成,两个指针分别指向字符要被插入或被取出的位置。 streambuf类定义了一组缓冲或处理流的通用方法,诸如设置缓冲区,移动指针,存、取字符等。
7
streambuf类有三个派生类,其中:
(1) conbuf在constream.h中定义,提供光标控制、清屏,定义活动窗口等控制台操作接口和I/O缓冲区管理功能。 (2) filebuf在fstream.h中定义,它用来维护文件缓冲区的打开,关闭,读、写,建立磁盘文件的内存代理。 (3) strstreambuf在strstream.h中定义,提供在内存进行提取和插入操作的缓冲区管理。
8
2. 输入输出流类 iostream.h中还有以ios类为基类的一组流类的定义。 ios类及其派生类均含有一个指向streambuf类的指针。通过streambuf类代理物理设备的操作。 从类ios开始,逐级派生,形成了较为复杂的结构,要很好地使用C++流类,必须逐级了解其父类的public、和protected类型的成员函数、成员变量。
9
C++ I/O流类的结构(2) istream_withassign ostream_withassign strstreambase
ifstream istrstream ofstream ostrstream fstream strstream iostream iostream_ withassign istream ostream fstreambase ios 图11.2 ios类的派生关系
10
流类库成员列表 流类分类 流类名称 流类作用 流基础类 ios 所有流类的父类、保存流的状态并处理错误 输入流类 istream
输入流基础类、将流缓冲区中的字符作格式化和非格式化之间的转换,并输入。 ifstream 文件输入流类 istream_withassign 为cin输入流类即操作符>>输入流 istrstream 串输入流类 输出流类 ostream 输出流的基础类、将流缓冲区中的数据格式化或非格式化转换,并输出。 ofstream 文件输出流类 ostream_withassign cout,cerr,clog输出的流类即操作符<<输出流。 ostrstream 串输出流类 文件流类 fstreambase 文件流基础类 串流 strstreambase 串流基础类 输入/输出流类 iostream 多目的输入/输出流类的基础类 fstream 文件流输入/输出类 strstream 串流输入/输出类 流类库成员列表
11
上表中: (1) 基础类ios是所有流类的根。它有四个直接派生类:istream、ostream、fstreambase和strstreambase,它们是流类库中的基本流类。 (2) istream和ostream是ios直接派生类,其功能是将数据格式化或非格式化数据与流缓冲区之间作转换处理。 (3) istream_withassign,ostream_withassign分别从istream和ostream类派生,并定义了cin,cout流,作标准输入和输出处理。 (4) 而iostream是istream和ostream类的多继承派生类,用来操作文件流的标准输入/输出。 (5) ifstream是istream和fstreambase的多继承派生类,用于处理文件的输入。 (6) ofstream是ostream和fstreambase的多继承派生类,用于处理文件的输出。
12
11. 2 C++输入与输出 (1)类输入/输出,把数据作为单字符流处理。可以处理简单的数据,也可以处理复杂的数据结构。
(2)直接调用操作系统的底层功能实现输入/输出操作。 每一类都有文件和标准的输入和输出功能。这里着重讨论使用流类的标准输入和输出,以及流类的文件输入和输出。
13
11.2.1标准输入输出 istream_withassign cin; ostream_withassign cout;
标准输入与输出是指读键盘的数据和将数据输出到屏幕。 C++用istream_withassign类和ostream_withsaaign类来实现标准输入输出功能。 iostream.h文件中以下两句定义cin和cout两个标准流对象: istream_withassign cin; ostream_withassign cout; 在标准输入类istream_withassign和标准输出类ostream_withassign分别对操作符“>>” “<<”进行了重载,用于完成对各种简单数据类型的输入和输出。
14
11.2.2格式化的输入输出 ios类中定义的格式控制成员函数 基于流对象的操纵函数。 C++提供了两种格式控制函数用来控制输入输出的格式:
相比之下操纵函数使用更为方便。下面重点介绍操纵函数的概念和用法。
15
1.ios 类中定义的格式控制标志 ios 类中定义了一个数据成员:格式控制标志字long x_flags。x_flags每一位的状态值用枚举符号常量定义。 enum{ skipws =0x0001, //跳过输入空格 left =0x0002, //输出左对齐调整 right =0x0004, //输出右对齐调整 internal =0x0008, //输出符号和基指示符后的填补 dec =0x0010, //转换为十进制 (in/out) oct =0x0020, //转换为八进制 (in/out) hex =0x0040, //转换为十六进制 (in/out) showbase=0x0080, //输出显示基指示符 showpoint=0x0100, //输出显示小数点 uppercase=0x0200, //大写十六进制输出 showpos =0x0400, //正整数显示前加上”+” scientific=0x0800, //输出用科学表示法表示浮点数 fixed =0x1000, //输出用固定小数点表示浮点数 unitbuf =0x2000, //在输出操作后刷新所有流 stdio =0x //在输出后刷新stdout和stderr };
16
long x_flags = 0x0080 = 输出显示基指示符 long x_flags = 0x0003 = 跳过输入空格 & 输出左对齐调整 long x_flags = 0x0083 = 跳过输入空格 & 输出左对齐调整 & 输出显示基指示符 long x_flags标志可以由ios类的成员函数 flags(), setf()和unsetf()访问,也可以用操纵函数操作。
17
2.操纵函数和操纵符 操纵函数分为带参数和不带参数两种。不带参数的操纵函数又叫操纵符。操纵符定义在头文件iostream.h中,操纵函数定义在头文件iomanip.h中.
18
表 11.2 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; 忽略输入流中的空格 endl cout>>endl; 插入换行符,刷新流 ends cout>>ends; 插入串最后的串结束符 flush cout>>flush; 刷新一个输入流
19
表 11.3 iomanip.h中的操纵函数 setprecision(int) cout<<setprecision(6)
cin>>setprecision(15) 输出浮点数精度为6位小数 输入浮点数精度为15位小数 setw(int) cout<<setw(6)<<var; cin>>setw(24)>>buf; 输出数据宽度为6 输入数据宽度为24 setiosflags(long) cout<<setioflags(ios::hex| ios::uppercase) cin>>setioflags(ios::oct| ios::skipws) 指定数据输出的格式为十六进制格式且用大写字母输出 指定数据输入的格式为八进制格式且跳过输入中的空白 resetiosflags(long) cout<< resetiosflags(ios::dec) cin>>resetiosflags(ios::hex) 取消数据输出的格式为十进制格式 取消数据输入的格式为十进制格式
20
例11. 1 操纵符的使用 #include <iostream.h> #include <iomanip.h> void main() { double values[] = { 1.23, 35.36, 653.7, }; char *names[] = { "Zoot", "Jimmy", "Al", "Stan" }; cout << setiosflags( ios::fixed );//浮点数使用普通记数法表示 for ( int i = 0; i < 4; i++ ) cout << setiosflags( ios::left) //设置左对齐 << setw(6) //设置数据宽度为6位 << names[i] << resetiosflags( ios::left ) //撤消左对齐 << setw( 10 ) //设置数据宽度为10位 << setprecision(1) //设置浮点数精度1位 << values[i] << endl; } 输出为: Zoot Jimmy Al Stan
21
11. 3流类运算符重载 使用传统的C语言中printf,scanf等输入输出函数编写的程序,当输入输出的数据类型发生变化时,必须重写所有的输入输出代码。使用C++流类后,这个问题不复存在。 C++利用流类对象cin、cout的成员函数重载任意类的输入输出操作。 流的输入运算符”>>”又称提取运算符,流的输出运算符”<<”又称插入运算符,它们都可以重载。
22
例:用户定义的日期Date类的输入输出。
由于重载的函数需要访问Date类的私有的和受保护的成员,应该把这些重载的函数定义为Date类的友元。
23
例11. 2 用户定义Date数据类中对流类运算符<<、>>函数重载
#include <iostream.h> class Date{ int mo, da, yr; public: Date( int m, int d, int y ) { mo = m; da = d; yr = y; } friend ostream& operator<< ( ostream& os, Date& dt ); 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; return is; //返回流的引用 例11. 2 用户定义Date数据类中对流类运算符<<、>>函数重载 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; Date dt_input( 0, 0, 0 ); cin>> dt_input; cout << dt_input <<endl; }
24
重载操作符函数时应该注意以下几点: 重载的操作符函数有两个参数。
如果重载的是输出操作符<<,第一个参数是对流ostream类的引用,如果重载的是输入操作符>>,第一个参数是对流istream类的引用。 第二个参数是要定义输入输出类的引用,也可以是此类的指针。 运算符重载必须定义为类的友元,因为左操作数必须是流类对象而不是输入/输出类的对象,不能使用隐式左操作数。
25
# 查询 复杂数据、有查询 简单数据、有查询 对象-关系数据库(ORDB) 关系式数据库(RDB) 简单数据、无查询 简单数据、无查询
面向对象数据库(OODB) 简单数据、无查询 文本文件 复杂程度 #
26
11. 4文件流与文件操作 C++ 标准库文件输入与输出 标准的C++库提供了文件输入输出功能,它们是以函数的形式给出的。这里将对一些常用的函数如fopen 、fclose 、fwrite、fread等作简要说明。 标准的C库对文件输入输出功能函数定义在 stdio.h 头文件中,并且定义了一个FILE型文件结构,在每一个文件被打开时,都有一个FILE型文件指针与之关联,以便保存文件的相关信息,完成文件的读写操作。
27
对文件的操作一般分为三个步骤: (1)使用文件打开函数对文件进行打开,并与文件指针建立关系; (2)利用文件指针和写文件、读文件函数对文件进行操作。 (3)使用文件关闭函数,对文件关闭。
28
1 打开文件函数fopen 完成文件操作第一步,可使用fopen函数,该函数定义为:
FILE *fopen( const char *filename, const char *mode );其中参数 filename是文件名字符串,mode为文件打开的模式。使用时注意: 1).返回值是返回打开文件的指针,如果文件不存在或者不能找到文件,fopen调用返 回空指针(NULL)表示为文件打开失败。 2).参数mode 可取以下值 "r"只读方式打开文件。 "w"写方式打开文件,如果文件已经存在,其文件中的内容被破坏。 "r+"读和写的方式打开。(文件必须已经存在,否则将导致异常) "w+"读和写方式打开一空文件,如果该文件已经存在,其内容将被破坏。 "a+"为了读文件和附加新内容而打开文件。 "t" 以文本方式打开文件。 "b" 二进制的方式打开文件。
29
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 ); 使用时注意: (1)fread 函数读文件返回文件数据记录的数目; (2)fwrite 函数返回实际写入文件数据记录的数目; (3)参数buffer 指向数据缓冲区。fread函数将读出的数据放在缓冲区中供程序使用; fwrite函数将写入文件的数据放在冲区中以便写入文件。 (4)size为从文件读出和写入文件的字节数 (5)count一次从文件读出数据的最大记录数和一次写入文件的最大记录数。 (6)stream文件结构指针。fread 函数为打开文件的针; fwrite 函数为把数据要写入 文件的指针。
30
3 关闭文件函数fclose 完成第三步操作可以使用fclose,其格式为: int fclose( FILE *stream );// 关闭流文件(fclose) int _fcloseall( void ); //关闭所有的流(_fcloseall) 使用时注意: (1)fclose函数返回值为0 时表示文件成功关闭,否则返回值为非0。 (2)fcloseall成功关闭所打开的文件时,函数返回值为关闭文件的总数。 (3)fclose函数的参数为指向FILE结构体的指针。
31
例11. 3 打开一个名为 FREAD.OUT 的文件并写入25 个字符, 然后将从文件中读出的字符
#include <stdio.h> void main() { FILE *stream; char list[30]; int i, numread, numwritten; // 文本模式打开文件并写数据 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( "Wrote %d items\n", numwritten ); fclose( stream ); } else printf( "Problem opening the file\n" ); /*读出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 ); printf( "File could not be opened\n" ); 输出: Wrote 25 items Number of items read = 25 Contents of buffer = zyxwvutsrqponmlkjihgfedcb
32
11.4.2文件输入输出流 在C ++中,对文件的输入输出操作提供了另一种操作方式,即流类库的文件操作,这些类是ofstream与ifstream文件输入输出流类。其对文件操作也需要上述的三个步骤。 1. 文件的输出 文件的输出由ofstream完成,它由ostream类继承而来(见图11.2),并继承了它的操作,因此可以利用ostream重载的操作符<<把数据写入文件流中。进行文件输出时首先要创建文件输出流ofstream类的一个对象,ofstream类的构造函数用于在不同情况下构造文件流。下面是ofstream类造函数的定义: ofstream(); ofstream(const char FAR *,int=ios::out,int=filebuf::openprot);
33
(1)文件的打开 第一个构造函数用于构造一个不带参数的流,如果需要可以在用它的open函数打开一个文件,下面是open函数的定义: void open(const signed char_FAR *,int=ios::in,int=filebuf::openprot); 它需要三个参数,第一个参数为字符串,为打开文件的文件名;第二个参数为文件模式,缺省为ios::out,表示为输入打开,其它文件模式见表11.4; 第三个参数为文件的保护方式它们有三种取值0表示普通文件、1表示只读文件、2表示隐含文件、4表示系统 文件、8表示文档位格式。例如对打开一个test1.dat文件可用如下语句: ofstream afile(); afile.open("test1.dat");或afile.open("test1.dat",ios::out|ios::binary) 第二个构造函数可以在创建文件时通过第一个参数直接指定文件名。第二个参数、第三个参数同上述open函数。 对打开一个test1.dat文件可用如下语句: ofstream afile("test1.dat"); ofstream afile("test1",ios::noreplace); ofstream afile("test1",ios::append); ofstream afile("test1",ios::out|ios::binary);
34
表11.4 文件模式 模式 功能说明 Ios:app 追加数据,总是加在源文件的尾部 Ios::ate 在打开的文件上找到文件尾
Ios::in 为输入打开文件(默认对ifstream适用) Ios::out 为输出打开文件(默认对ofstream适用) Ios::binary 打开二进制文件 Ios::trunc 如文件存在则消去原内容 Ios::nocreate 如文件不存在,打开失败 Ios::noreplace 如文件存在,打开失败,除非设置了ate和app
35
2). 文件的写入数据函数 ofstream类是从ostream类继承而来,因而可以使用ostream类的写数据的函数和操作符把数据写入文件,操作符函数在前面已经介绍,这里实现写数据的函数是: class _CRTIMP ostream : virtual public ios {…… public: //写入字符文件 inline ostream& put(char); ostream& put(unsigned char); inline ostream& put(signed char); //写入字符串str的内容到文件 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(); ……. }
36
3). 文件的关闭函数 ofstream类也是从fstreambase类继承的,可以使用fstreambase类的close();来关闭文件。
37
2. 文件的输入 文件的输入由类ifstream完成,它由iostream和fstreambase类继承而来,并继承了它的操作符>>函数和文件打开,从文件中读数据、关闭文件的函数。ifstream也提供了构造函数: ifstream(); ifstream(const cahr_FAR *,int=ios::in,int=filebuf::openprot);
38
1).文件的打开 第一个构造函数用于构造一个不带参数的流,如果需要可以在用它的open函数打开一个文件,下面是open函数的定义: void open(const signed char_FAR *,int=ios::in,int=filebuf::openprot); 它和第二构造函数的使用同文件的输入中的文件打开。 2). 文件的数据读出函数 可以使用istream类的写数据的函数和操作符把数据写入文件,操作符函数在前面已经介绍,这里实现写数据的函数是: class _CRTIMP istream : virtual public ios {…… public: //读取文件数据到字符串内 inline istream& get(char *,int,char ='\n'); inline istream& get(unsigned char *,int,char ='\n'); inline istream& get(signed char *,int,char ='\n'); //读取文件数据到字符内 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); istream& read(char *,int); inline istream& read(unsigned char *,int); inline istream& read(signed char *,int); int gcount() const { return x_gcount; } int peek(); istream& putback(char); int sync(); //移动输入文件指针位置 istream& seekg(streampos); istream& seekg(streamoff,ios::seek_dir); //读取当前输入文件指针位置的值 streampos tellg(); …… }
39
3). 文件的关闭函数 ifstream类也是从fstreambase类继承的,可以使用fstreambase类的close();来关闭文件。
40
例11. 4使用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联系 ofstream fout(“file.out”);//创建一输出流,并和输出文件file.out联系 if(!fin) cerr<<”Cannot open file file.in”; exit(-1); //如果原文件出错,显示信息并退出 } if (!fout) cerr<<”Can’t open file file.out”; exit(-1); //如果目的文件出错,显示信息并退出 while (fout&&fin.get(ch)) fout.put(ch);
41
例11. 5 利用istream和ostream类的操作符对点数据进行文件操作,从键盘上输入两个点的坐标,把它保存到磁盘文件上,并从磁盘上读出数据,写到显示器上。
#include <stdio.h> #include <conio.h> #include <istream.h> #include <ostream.h> #include <fstream.h> #include <process.h> class Point { protected: int X; int Y; public: Point(int x,int y){X=x;Y=y;} 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; return is; //返回流的引用 int main() int x,y; ofstream writefile("cstest.sav"); //创建一个文件流 if (!writefile) //如果创建失败,退出 { cout <<"Can't open file"; exit(-1); cout <<"input the x、y with blank seperated:"; //标准流输出提示 cin>>x>>y; //标准输入流输入数据 Point Point1(x,y); //创建点对象 cout<<"input x、y of another point:"; //提示输入另一个点 cin>>x>>y; //标准输入流输入数据 Point Point2(x,y); //创建点对象 writefile<<Point1; //把第一个点存入文件 writefile<<Point2; //把第二个点存入文件 writefile.close(); ifstream readfile("ctest.sav"); //创建一个输入文件流 if(!readfile) //如果创建失败,退出, cout<<"Can't open file"; readfile>>Point1>>Point2; //把文件的数据读入点对象 cout<<Point1<<endl; //将第一个点对象写在显示器上 cout<<Point2<<endl; //将第二个点对象写在显示器上 return 0;
Similar presentations