1 / 71

第 9 章 I/O 流类库

第 9 章 I/O 流类库. 主要内容 基本概念 C++ 的基本流类结构 Istream 类和 ostream 类 格式控制 文件的读 / 写 可流类. 输出. 输入. 9.1 基本概念. 设备间的数据传送 内存 显示屏 内存 文件 键盘 内存 文件 内存 键盘 文件 流 面向对象技术中,任何设备都可以表示为相应类的对象,设备之间的数据传送即对象之间的数据传送。

evelyn
Download Presentation

第 9 章 I/O 流类库

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第9章 I/O流类库 主要内容 • 基本概念 • C++的基本流类结构 • Istream类和ostream类 • 格式控制 • 文件的读/写 • 可流类

  2. 输出 输入 9.1 基本概念 • 设备间的数据传送 • 内存 显示屏 • 内存 文件 • 键盘 内存 • 文件 内存 • 键盘 文件 • 流 • 面向对象技术中,任何设备都可以表示为相应类的对象,设备之间的数据传送即对象之间的数据传送。 • 数据从源对象到目的对象的传送可以抽象看做一个“流”。 • 当键入字符时,就可认为字符从键盘流入程序的数据结构中;

  3. 当写入磁盘文件时,也可认为程序流到磁盘上。当写入磁盘文件时,也可认为程序流到磁盘上。 例如: cout<<“数据”; cin>>变量名; • 流类 • 把实现设备之间信息交换的类称作流类。 • 流库 • 若干流类的集合称做流类库。 • C++流类库是用继承方法建立起来的一个输入/输出类库,它具有两个平行的基类即streambuf类和ios类,所有其它的流类都是从它们直接或间接地派生出来的。

  4. 9.2 C++的基本流库结构 • C++为何建立自己的输入输出系统 • C语言的输入/输出库函数不支持用户自定义的数据类型(如结构、类) ,用户也不能通过重载这些库函数来实现用户自定义数据类型的输入/输出。例如: struct my_struct{ int i; float f; char *str; }s; 如果:printf(“%my_struct”, s); //错误 因为printf只能识别系统预定义的类型。

  5. C++流库结构 • 每个流都是一种与设备相联系的既有状态,又有操作的对象,即流对象。对流对象进行抽象就得到流类,流类形成的层次结构就构成流类库(或流库)。 • C++流库主要由两个流类层次组成: • 以streambuf类为父类的类层次 • 主要完成信息通过缓冲区的交换。 • 以ios类为父类的类层次 • 在streambuf类实现功能的基础上,增加了各种格式化的输入/输出控制方法。

  6. streambuf filebuf strstreambuf stdiobuf streambuf类的派生层次 • C++流类库示意图: • streambuf类层次结构 • 缓冲区:是一个队列数据结构,由一字符序列和两个指针组成,这两个指针分别指向字符要被插入或被取出的位置。 • streambuf类为所有的streambuf类层次对象设置了一个固定的内存缓冲区,动态划分为两部分: • 用做输入的取区,用取指针指示当前取字符位置。 • 用做输出的存区,用存指针指示当前存字符位置。

  7. filebuf类 • 是在fstream.h中定义的streambuf类的一个派生类,它使用文件来保存缓冲区中的字符序列。 • 将filebuf同某个文件的描述字相联系就称打开这个文件。 • 当写文件时,是将缓冲区的字符写到指定的文件中,之后刷新缓冲区; • 当读文件时,是将指定文件中的内容读到缓冲区中来。

  8. strstreambuf类 • 扩展了streambuf类的功能,增加了动态内存管理功能,提供了在内存中进行提取和插入操作的缓冲区管理。 • stdiobuf类 • 主要用作C++语言的流类层次方法和C语言的标准输入/输出方法混合使用时系统的缓冲区管理。 在通过情况下,均使用这三个派生类,很少直接使用streambuf 类。

  9. ios类层次结构 • ios类及其派生类是在streambuf类实现的通过缓冲区的信息交换的基础上,进一步增加了各种格式化的输入/输出控制方法。它们为用户提供使用流类的接口,它们均有一个指向streambuf的指针。 • ios类有四个直接派生类: istream ostream fstreambase strstreambase 这四种流作为流库中的基本流类。

  10. istrstream istream_withassign istream ifstream strstream ios iostream stdiostream fstream ostream ofstream ostream_withassign ostrstream • ios类层次: ios类的派生层次

  11. 从上图可看出各个类的继承关系,如: class ios; class istream : virtual public ios; class ostream : virtual public ios; class iostream : public istream, public ostream; 在istream类、ostream类和iostream类的基础上,分别重载赋值运算符“=”,就派生出istream_withassign、 ostream_withassign和ipstream_withassign类,即: class istream_withasssign : public istream; class ostream_withasssign : public ostream; class iostream_withasssign : public iostream;

  12. ios虚基类 主要完成流的状态设置、状态报告、显示精度、域宽、填充字符的设置,文件流的操作模式定义等。 • istream类和ostream类 对运算符>>和<<进行重载。 • iostream类 以istream类和ostream类为基类,多重继承派生。可同时进行输入输出操作。

  13. 9.3 istream类和ostream类 • 在编写C++程序时,使用标准的输入/输出流cin和cout进行输入/输出,是因为开始执行C++程序时,C++会自动打开几个预定义流: • 标准输入流cin(缺省为键盘) • 标准输出流cout(显示终端) • 非缓冲型的标准出错流cerr(显示终端) • 缓冲型的标准出错流clog(显示终端) 它们在iostream.h中说明为_withassign类的对象: extrean iostream_withassign cin; extrean iostream_withassign cout; extrean iostream_withassign cerr; extrean iostream_withassign clog;

  14. istream类 • 在流库中提供主要的输入操作,默认对象是cin; • istream类是在include目录下的文件istream.h中,istream类中定义的运算符和成员函数包括输入运算符>>、get函数、getline函数、read函数等。 • istream类的定义:

  15. class istream : virtual public ios{ public: istream(streambuf *); istream & operator>>(char *); istream & operator>>(int &); inline istream & operator>>(unsigned char *); int get(); istream & get(signed char *, int, char = ‘\n’); istream & get(streambuf &, char = ‘\n’); istream & get(unsigned char &); istream & getline(signed char *, int, char = ‘\n’); int peek(); //表示不输入并返回下一个字符 int gcount(); //用来返回上次输入的字符数 istream & read(signed char *, int); … };

  16. >>叫做输入运算符,将数据从左边的流中读出。>>叫做输入运算符,将数据从左边的流中读出。 int i; cin>>i; //cin.operator>>(i); • 重载的输入运算符“>>”均返回流类istream的引用,因此输入运算符可连用。例如: int x; char y; cin>>x>>y; • 在读入一个字符串时,空格将作为一个串的终止。如: char ch[20]; cin>>ch; 键盘输入 great wall ch中仅存放great.

  17. 缺省情况下,输入运算符在读取数据时,跳过空白字符,如: 12345 //读入时跳过前面的空格 • 不同类型的变量一起输入时,系统除检查是否有空白外,还检查输入数据与匹配情况。例如: int i; foat x; cin>> i >> x; 若输入: 56.79 32.85 实际结果:i = 56, x = 0.79

  18. 系统用数据类型分隔输入数据,如果读取的数据类型与输入运算符的参数类型不符,它将返回零值,并终止输入,如:系统用数据类型分隔输入数据,如果读取的数据类型与输入运算符的参数类型不符,它将返回零值,并终止输入,如: int v[3]; for(int i=0; i<3; i++) { if(cin>>v[i]) { cout<<"v["<<i<<"]= " <<v[i]<<endl; continue; } cout<<i<<endl; } 键盘输入:11gh55 程序运行结果: v[0]=11 1 2

  19. 例9.1 设计一个从键盘输入中提取若干个字符或字符串的例子。 #include<iostream.h> void main(void) { int length = 6; char a, b[6], c[6]; cin.get(a); cin.get(); //跳过输入流中1个字符 cin.getline(b, length); cin.get(); cin.getline(c, length); cout<<a<<“”<<b<<“”<<c<<endl; }

  20. 为什么可直接使用“cin>>”进行输入? • 当开始执行C++程序时,系统自动为其建立一个istream_withassign类的对象cin。 • 而istream_withassign是istream的派生类,它继承了基类的所有公有成员,在istream类中重载了几乎所有系统预定义类型的输入运算符“>>”。 • 对象cin可调用这些“>>”,因此用户可直接使用“cin>>”输入系统预定义类型的变量。

  21. ostream类 • ostream类在流库中提供主要的输出操作,默认对象是cout。 • ostream类是在include目录下的文件ostream.h中,ostream类中定义的运算符和成员函数包括输出运算符<<、put函数、write函数等。 • ostream类定义:

  22. class ostream : virtual public ios{ public: ostream(streambuf * ); ostream & operator<<(const char *); ostream & operator<<(int); inline ostream & put(char); ostream & write(const char *, int); //… };

  23. <<叫做输出运算符,将运算符右边的数据存储到左边的流中。<<叫做输出运算符,将运算符右边的数据存储到左边的流中。 cout<<“string”; cout.operator<<(“string”); • 所有重载的输出运算符均返回ostream的引用,利用该引用可继续调用下一个输出运算符函数,因而在一条语句中可以显示多个数据,如: int x; cout<<“x=“<<x; 系统执行如下:输出运算符按自左至右的顺序 cout.operator<<(“x=“).operator<<(x)

  24. 输出运算符重载之后,没有改变其优先级,如果输出的数据含有表达式,应注意运算顺序,如:输出运算符重载之后,没有改变其优先级,如果输出的数据含有表达式,应注意运算顺序,如: cout<<x+y<<‘\n’;//正确,+的优先级高 cout<<x&y<<endl;//y是非法流,&的优先级低 结合顺序实际为: (cout<<x)&(y<<‘\n’) //error 应该用括号改变顺序: cout<<(x&y)<<‘\n’;

  25. struct mytype{ int i; float f; char c; }; mytype mt; cin>>mt; cout<<mt; 对于用户自定义结构体类型或类类型的输入或输出,在C++中可以通过重载运算符“<<”和“>>”来实现。 例9.2 设计一个包含几种典型情况的屏幕输出的例子。 P197

  26. 重载输出运算符“<<” • 通过重载输出运算符“<<”来实现用户自定义类型的输出,定义格式为: ostream & operator<<(ostream &os, user_type &d) { os<<d.item1; os<<d.item2; os<<d.item3; //… return os; } 下面举一个具体的例子:

  27. #include<iostream.h> class three_d{ int x, y, z; public: three_d(int x1, int y1, int z1) : x(x1), y(y1), z(z1){} //… friend ostream & operator<<(ostream &, three_d & obj); }; ostream & operator<<(ostream &output, three_d & obj) { output<<“x=“<<obj.x<<endl; output<<“y=“<<obj.y<<endl; output<<“z=“<<obj.z<<endl; return output; } void main(void) { three_d obj1(10, 20, 30); cout<<obj1; }

  28. 说明: • 重载的operator<<函数必须返回一个输出流类ostream对象的引用,这样,重载的输出运算符“<<”可连用。 • 输出运算符“<<”是双目运算符,使用时必须是输出流对象为第一操作数,输出数据为第二操作数,次序不能改变。 • 一般情况下,重载输出运算符函数不能是类的成员函数。(因为如果一个运算符函数是类的成员,则其左运算数就应当是调用运算符函数的类的对象。但重载运算符时,其左边参数是流,而右边的参数是类的对象。)

  29. 重载输入运算符“>>” • 通过重载输入运算符“>>”来实现用户自定义类型的输入,定义格式为: istream & operator>>(istream &is, user_type &d) { is>>d.item1; is>>d.item2; is>>d.item3; //… return is; } 下面举一个具体的例子:

  30. #include<iostream.h> class three_d{ int x, y, z; public: three_d(int x1, int y1, int z1) : x(x1), y(y1), z(z1){ } void print(){cout<<x<<" "<<y<<" "<<z<<endl;} //… friend istream & operator>>(istream &, three_d & obj); }; istream & operator>>(istream &input, three_d & obj) { input>>obj.x; input>>obj.y; input>>obj.z; return input; } void main(void) { three_d obj1(10, 20, 30); obj1.print(); cin>>obj1; obj1.print(); }

  31. 说明: • 重载的operator>>函数必须返回一个输入流类istream对象的引用,这样,重载的输入运算符“>>”可连用。 • 与重载输出运算符函数一样,重载输入运算符函数也不能是所操作类的成员函数,但可以是该类的友元函数或独立函数。

  32. 9.4 格式控制 • C++语言提供了两种格式控制方法 • 利用ios类中的格式控制成员函数 • 利用操作符

  33. 格式控制成员函数 • 状态设置和状态报告 • ios类中定义的long类型的保护数据成员x_flag用于存入控制输入输出格式的状态标志。 • 状态标志位共15个,各占一个二进制位,称为格式状态位。 • 若设置了某个状态标志位,则x_flags中的某一位为“1”,否则为“0”。 例如:若在状态标志中设定了skipws和dec,其它均未设定,则x_flags的值应为0000000000010001, 即为十六进制的0x0011。 这些状态值之间是或的关系,可以几个并存。

  34. 设置状态标志 long ios::setf(long flags) { x_flags = x_flags | flags; //(按位或) return x_flag; } 功能:把指定位的状态标志设置为1。 调用如:ut<<setf(ios::hex||ios::scientific); • 清除状态标志 long ios::unsetf(long flags) { x_flags = (x_flags) & (~flags); return x_flags; } 功能:把指定位的状态标志设置为0。

  35. 取状态标志 long ios::flags() const { return x_flags; } 功能:返回当前流的状态标志位数值。 long ios::flags(long flags) { long x = x_flags; x_flags = flag; return x; } 功能:设置指定位的状态标志,并返回设置前的状态标志位数值。 • 与setf()的差别:setf()是在原有的基础上追加设定,而flags(long flags)是用新设定覆盖以前的状态标志。

  36. 显示精度设置 在ios类中用x_precision来存放浮点数的输出显示精度。 int ios::precision() { return x_precision; } 功能:返回当前的显示精度 int ios::precision(int n) { int x = x_precison; x_precison = n; return x; } 功能:设置显示精度为n位, 并返回设置前的显示精度。

  37. 域宽设置 用于用户控制输出格式,在ios类中用x_width存放。 int ios::width() { return x_width; } 功能:返回当前的域宽值 int ios::width(int n) { int i = x_width; x_width = n; return i; } 功能:设置域宽为n位

  38. 填充字符设置 在ios类中用x_fill来存放填充字符,填充字符的作用是当输出值不满域宽时用填充字符来填充,缺省情况下填充字符为空格。与width()函数配合使用。 char ios::fill() { return x_fill; } 功能:返回当前的填充字符值 char ios::fill(char c) { char x = x_fill; x_fill = c; return x; } 功能:设置填充字符为c

  39. x_width=0 123 x_precision=6 123 123.79 345.787 11 10 x_width=0 12 12 x_width=12 int i = 10; cout<<i<<' ' <<i++<<endl; cout<<i<<' '<<++i<<endl; cout<<"x_width="<<cout.width()<<endl; cout<<"x_width="<<cout.width(10)<<endl; cout.width(12); cout<<"x_width="<<cout.width()<<endl; cout<<123<<endl; cout<<"x_precision="<<cout.precision()<<endl; cout<<123<<' '<<123.78985<<' '<<345.787<<endl;

  40. 说明: • 未设置域宽时,x_width取值为0,当用width(n)设置域宽后,它只对最近跟着它的第一个输出有影响,第一个输出完成后,x_width 马上被自动置为0。 • x_precision与实际输出数值的精度不一致时,最终输出结果为: • 若实际输出数值的精度大于x_precision,则以x_precision作为精度四舍五入后输出; • 若实际输出数值的精度小于x_precision,则按实际精度输出。

  41. 操作符 • 使用ios类的成员函数来控制输入/输出格式时,必须缀上使用它的流对象(见例11.3),而且不能将它们直接嵌入到输入/输出语句中去。为此,C++还提供了操作符方法。 • 操作符是一个函数,它以一个流引用作为参数,并返回同一流的引用。因此它可以嵌入到输入/输出操作的链中。例如,hex操作符的函数体定义: ios & hex(ios & o) { o.setf(ios::hex); return o; }

  42. C++提供的预定义的标准操作符。(见表11.1) • 无参数操作符 (P200表9.1) 例9.4 • 有参数操作符 (P201表9.2) 例9.5

  43. 9.5 文件的读/写 • 基本概念 • C++把文件看作字符/序列,即所谓流式文件。 • 根据数据的组织形式,文件可分为文本文件和二进制文件。 • 文本文件又称ASCII文件,它的每个字节存放一个ASCII代码,代表一个字符。这样便于对字符进行处理,也便于输出,但占用存储空间较多。 • 二进制文件是把内存中的数据,按其在内存中的存储形式原样写到磁盘上。用二进制形式输出数据,可节省存储空间和转换时间,但一个字节并不对应一个字符。一般情况下,中间结果常用二进制文件保存。 • 按操作方法,文件可分为输入文件和输出文件。

  44. 打开的文件 其它设备(变量) 文件流的写 • 文件流是文件与内存等设备之间的信息交换过程。 注意: • 文件流的读对应内存变量的输入 • 文件流的写对应内存变量的输出 文件流的读

  45. C++中文件输入/输出的基本过程 • 在程序中要包含头文件fstream.h。 • 创建一个流。 • 将这个流与文件相关联,即打开文件。 • 进行文件的读写操作。 • 关闭文件。

  46. 9.5.1 文件的打开和关闭 • 文件的打开 • C++有三种类型的文件流: • 输入文件流ifstream • 输出文件流ofstream • 输入/输出文件流fstream 这些文件流类都定义在fstream.h文件中。各有4个重载的构造函数。 • 在C++中,打开一个文件,就是将这个文件与一个流建立关联;

  47. 打开文件的两种方式 • 在打开文件时要定义流类的对象,三个类的常用构造函数分别为: ifstream(const char *, int =ios::in, int =filebuf::openprot); ofstream(const char *, int =ios::out, int =filebuf::openprot); fstream(const char *, int, int =filebuf::openprot); 参数含义: 字符串形式的文件名、文件流的操作模式、打开文件的共享/保护模式。

  48. 一旦定义了一个对象,就建立了一个外存介质上的文件和内存的交换通道,并指定了数据交换的方向。一旦定义了一个对象,就建立了一个外存介质上的文件和内存的交换通道,并指定了数据交换的方向。 例如: ifstream ifs(“Data1.dat”); ofstream ofs(“Data2.dat”); fstream iofs(“Data3.data”, ios::in|ios::out); • ifstream类、ofstream类和fstream类中提供了专门的打开成员函数。 void ifstream::open(const char *, int =ios::in, int =filebuf::openprot); void ofstream::open(const char *, int =ios::out, int =filebuf::openprot); void fstream::open(const char *, int, int =filebuf::openprot);

  49. 例如: ifstream ifs; ofstream ofs; fstream iofs; ifs.open(“Data1.dat”); ofs.open(“Data2.dat”); iofs.open(“Data3.dat”, ios::in|ios::out); • 打开当前工作目录下的指定的文件; • 如果当前工作目录下不存在指定文件,则在当前工作目录下创建一个新的指定文件。 • 可指定文件目录,如: ifstream ifs(“e:\mytest\Data1.dat”);

  50. 文件的关闭 • 关闭文件即是使打开的文件与流“脱钩”。使用流类的成员函数close()完成,它不带参数,无返回值。 例如: out.close(); 将关闭与流out相连接的文件。 • 关闭文件的作用 • 把要写入文件的数据从缓冲区中完全写回磁盘。(内存和文件的数据交换是通过缓冲区完成的。) • 保证文件安全。

More Related