第 9 章 I/O 流类库

71
1 第 9 第 I/O 第第第 第第第第 1. 第第第第 2. C++ 第第第第第第第 3. Istream 第第 ostream 第 4. 第第第第 5. 第第第第 / 第 6. 第第第

description

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

Transcript of 第 9 章 I/O 流类库

Page 1: 第 9 章  I/O 流类库

1

第 9 章 I/O 流类库主要内容

1. 基本概念2. C++的基本流类结构3. Istream类和 ostream类4. 格式控制5. 文件的读 /写6. 可流类

Page 2: 第 9 章  I/O 流类库

2

9.1 基本概念 设备间的数据传送

内存 显示屏 内存 文件 键盘 内存 文件 内存 键盘 文件

流 面向对象技术中,任何设备都可以表示为相应类的对象,设备之间的数据传送即对象之间的数据传送。 数据从源对象到目的对象的传送可以抽象看做一个“流”。

当键入字符时,就可认为字符从键盘流入程序的数据结构中;

输出输入

Page 3: 第 9 章  I/O 流类库

3

当写入磁盘文件时,也可认为程序流到磁盘上。例如:

cout<<“ 数据” ; cin>> 变量名;

流类 把实现设备之间信息交换的类称作流类。

流库 若干流类的集合称做流类库。 C++ 流类库是用继承方法建立起来的一个输入 / 输出类库,它具有两个平行的基类即 streambuf 类和 ios 类,所有其它的流类都是从它们直接或间接地派生出来的。

Page 4: 第 9 章  I/O 流类库

4

9.2 C++ 的基本流库结构1. C++ 为何建立自己的输入输出系统

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

Page 5: 第 9 章  I/O 流类库

5

2.C++ 流库结构 每个流都是一种与设备相联系的既有状态,又有操作的对象,即流对象。对流对象进行抽象就得到流类,流类形成的层次结构就构成流类库(或流库)。 C++ 流库主要由两个流类层次组成:

以 streambuf 类为父类的类层次 主要完成信息通过缓冲区的交换。

以 ios 类为父类的类层次 在 streambuf 类实现功能的基础上,增加了各种格式化的输入 / 输出控制方法。

Page 6: 第 9 章  I/O 流类库

6

C++ 流类库示意图:1)streambuf 类层次结构

缓冲区:是一个队列数据结构,由一字符序列和两个指针组成,这两个指针分别指向字符要被插入或被取出的位置。 streambuf 类为所有的 streambuf 类层次对象设置了一个固定的内存缓冲区,动态划分为两部分:

用做输入的取区,用取指针指示当前取字符位置。 用做输出的存区,用存指针指示当前存字符位置。

streambufstrstreambuf stdiobuffilebuf

streambuf类的派生层次

Page 7: 第 9 章  I/O 流类库

7

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

Page 8: 第 9 章  I/O 流类库

8

strstreambuf 类 扩展了 streambuf 类的功能,增加了动态内存管理功能,提供了在内存中进行提取和插入操作的缓冲区管理。

stdiobuf 类 主要用作 C++ 语言的流类层次方法和 C 语言的标准输入 / 输出方法混合使用时系统的缓冲区管理。

在通过情况下,均使用这三个派生类,很少直接使用 streambuf 类。

Page 9: 第 9 章  I/O 流类库

9

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

istream ostream fstreambase strstreambase

这四种流作为流库中的基本流类。

Page 10: 第 9 章  I/O 流类库

10

ios 类层次:

iosistream

ostreamiostream

istrstreamistream_withassignifstream

ostrstreamostream_withassignofstream

strstreamstdiostreamfstream

ios类的派生层次

Page 11: 第 9 章  I/O 流类库

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;

Page 12: 第 9 章  I/O 流类库

12

ios虚基类主要完成流的状态设置、状态报告、显示精度、域宽、填充字符的设置,文件流的操作模式定义等。

istream 类和 ostream 类对运算符 >>和 <<进行重载。

iostream 类以 istream 类和 ostream 类为基类,多重继承派生。可同时进行输入输出操作。

Page 13: 第 9 章  I/O 流类库

13

在编写 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;

9.3 istream 类和 ostream 类

Page 14: 第 9 章  I/O 流类库

14

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

Page 15: 第 9 章  I/O 流类库

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);…};

Page 16: 第 9 章  I/O 流类库

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.

Page 17: 第 9 章  I/O 流类库

17

缺省情况下,输入运算符在读取数据时,跳过空白字符,如: 12345 // 读入时跳过前面的空格

不同类型的变量一起输入时,系统除检查是否有空白外,还检查输入数据与匹配情况。例如:int i;foat x;cin>> i >> x;

若输入: 56.79 32.85 实际结果: i = 56, x = 0.79

Page 18: 第 9 章  I/O 流类库

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]=1112

Page 19: 第 9 章  I/O 流类库

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;}

Page 20: 第 9 章  I/O 流类库

20

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

Page 21: 第 9 章  I/O 流类库

21

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

Page 22: 第 9 章  I/O 流类库

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);//…};

Page 23: 第 9 章  I/O 流类库

23

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

Page 24: 第 9 章  I/O 流类库

24

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

( cout<<x)&(y<<‘\n’) //error 应该用括号改变顺序:

cout<<(x&y)<<‘\n’;

Page 25: 第 9 章  I/O 流类库

25

struct mytype{int i;float f;char c;};mytype mt;cin>>mt;cout<<mt; 对于用户自定义结构体类型或类类型的输入或输出,在C++ 中可以通过重载运算符“ <<” 和“ >>” 来实现。

例 9.2 设计一个包含几种典型情况的屏幕输出的例子。P197

Page 26: 第 9 章  I/O 流类库

26

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

Page 27: 第 9 章  I/O 流类库

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;}

Page 28: 第 9 章  I/O 流类库

28

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

Page 29: 第 9 章  I/O 流类库

29

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

Page 30: 第 9 章  I/O 流类库

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(); }

Page 31: 第 9 章  I/O 流类库

31

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

Page 32: 第 9 章  I/O 流类库

32

9.4 格式控制 C++ 语言提供了两种格式控制方法

利用 ios 类中的格式控制成员函数利用操作符

Page 33: 第 9 章  I/O 流类库

33

1.格式控制成员函数1)状态设置和状态报告

ios 类中定义的 long类型的保护数据成员 x_flag用于存入控制输入输出格式的状态标志。 状态标志位共 15个,各占一个二进制位,称为格式状态位。

若设置了某个状态标志位,则x_flags 中的某一位为“ 1” ,否则为“ 0” 。例如:若在状态标志中设定了 skipws 和 dec ,其它均未设定,则x_flags 的值应为 0000000000010001 ,即为十六进制的 0x0011 。这些状态值之间是或的关系,可以几个并存。

Page 34: 第 9 章  I/O 流类库

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。

Page 35: 第 9 章  I/O 流类库

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) 是用新设定覆盖以前的状态标志。

Page 36: 第 9 章  I/O 流类库

36

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

Page 37: 第 9 章  I/O 流类库

37

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

Page 38: 第 9 章  I/O 流类库

38

4)填充字符设置在 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

Page 39: 第 9 章  I/O 流类库

39

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;

123x_precision=6123 123.79 345.787

11 1012 12

x_width=0x_width=0 x_width=12

Page 40: 第 9 章  I/O 流类库

40

说明: 未设置域宽时, x_width 取值为 0,当用width(n)设置域宽后,它只对最近跟着它的第一个输出有影响,第一个输出完成后, x_width 马上被自动置为 0。 x_precision 与实际输出数值的精度不一致时,最终输出结果为:

若实际输出数值的精度大于 x_precision ,则以 x_precision 作为精度四舍五入后输出; 若实际输出数值的精度小于 x_precision ,则按实际精度输出。

Page 41: 第 9 章  I/O 流类库

41

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

ios & hex(ios & o){ o.setf(ios::hex);

return o; }

Page 42: 第 9 章  I/O 流类库

42

C++提供的预定义的标准操作符。(见表 11.1 )1) 无参数操作符 ( P200表 9.1 )

例 9.42) 有参数操作符 ( P201 表 9.2 )

例 9.5

Page 43: 第 9 章  I/O 流类库

43

9.5 文件的读 / 写 基本概念

1. C++ 把文件看作字符 / 序列,即所谓流式文件。2.根据数据的组织形式,文件可分为文本文件和二进制文件。

文本文件又称 ASCII 文件,它的每个字节存放一个ASCII代码,代表一个字符。这样便于对字符进行处理,也便于输出,但占用存储空间较多。 二进制文件是把内存中的数据,按其在内存中的存储形式原样写到磁盘上。用二进制形式输出数据,可节省存储空间和转换时间,但一个字节并不对应一个字符。一般情况下,中间结果常用二进制文件保存。

3.按操作方法,文件可分为输入文件和输出文件。

Page 44: 第 9 章  I/O 流类库

44

4. 文件流是文件与内存等设备之间的信息交换过程。

注意: 文件流的读对应内存变量的输入 文件流的写对应内存变量的输出

文件流的读打开的文件 其它设备 (变量 )文件流的写

Page 45: 第 9 章  I/O 流类库

45

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

Page 46: 第 9 章  I/O 流类库

46

9.5.1 文件的打开和关闭1. 文件的打开

C++ 有三种类型的文件流: 输入文件流 ifstream 输出文件流 ofstream 输入 / 输出文件流 fstream这些文件流类都定义在 fstream.h 文件中。各有 4个重载的构造函数。

在 C++ 中,打开一个文件,就是将这个文件与一个流建立关联;

Page 47: 第 9 章  I/O 流类库

47

打开文件的两种方式1) 在打开文件时要定义流类的对象,三个类的常用构造函数分别为: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);参数含义:字符串形式的文件名、文件流的操作模式、打开文件的共享 / 保护模式。

Page 48: 第 9 章  I/O 流类库

48

一旦定义了一个对象,就建立了一个外存介质上的文件和内存的交换通道,并指定了数据交换的方向。例如:ifstream ifs(“Data1.dat”);ofstream ofs(“Data2.dat”);fstream iofs(“Data3.data”, ios::in|ios::out);2) 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);

Page 49: 第 9 章  I/O 流类库

49

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

Page 50: 第 9 章  I/O 流类库

50

2. 文件的关闭 关闭文件即是使打开的文件与流“脱钩”。使用流类的成员函数 close() 完成,它不带参数,无返回值。例如:

out.close();将关闭与流 out 相连接的文件。

关闭文件的作用1) 把要写入文件的数据从缓冲区中完全写回磁盘。(内存和文件的数据交换是通过缓冲区完成的。)2) 保证文件安全。

Page 51: 第 9 章  I/O 流类库

51

3. 文件的操作模式 操作模式共8个,每种占一个二进制位。Emum open_mod{ in = 0x01, out = 0x02, ate = 0x04, app = 0x08, trunce = 0x10, nocreate = 0x20, noreplace = 0x40, binary = 0x80} ; 允许同时有一个以上的操作模式位为 1 。 文件打开的默认方式为文本方式。

Page 52: 第 9 章  I/O 流类库

52

9.5.2 文本文件的读 / 写文件打开后,即文件与流建立联系后,就可进行读写操作。文本文件支持文件的顺序访问。

1. 文本文件的读 文件打开的缺省方式是以文本格式打开,在输入时回车和换行两个字符要转换为字符‘ \n’; 在输出时,字符‘ \n’转换为回车和换行两个字符。 文件流的读对应内存变量的输入,即产生一个输入流。 ifstream 类的对象可以使用 ios 类中定义的输入运算符“ >>” 以及 get() 、 getline() 成员函数向内存变量“输入”信息。例 9.6 把当前工作目录下的 Data1.dat 文件中的字符数据显示在屏幕上。

Page 53: 第 9 章  I/O 流类库

53

用 get() 成员函数输入#include<fstream.h>#include<stdlib.h>void main(){ ifstream ifs(“data1.txt"); if(!ifs) { cerr<<"错误:无法打开文件 "<<endl; return; } char c; int n=0; while(ifs.get(c)) { cout.put(c); n++; } cout<<endl<<"字符个数 :"<<n<<endl; ifs.close(); }

文件内容:HowareyouThankyouGoodbye程序运行结果:Howareyou ThankyouGoodbye字符个数: 26

注意:‘ \n’转换与回车和换行字符之间的转换。

Page 54: 第 9 章  I/O 流类库

54

用 getline() 成员函数输入char buffer[20];int n= 0;while(!ifs.eof()){ ifs.getline(buffer, sizeof(buffer)); cout<<buffer<<endl; n++;}cout<<endl<<"行数: "<<n<<endl;

文件内容:HowareyouThankyou程序运行结果:HowareyouThank you行数: 2

Page 55: 第 9 章  I/O 流类库

55

用输入操作符“ >>” 把文件流中的数据输入到内存变量

说明:输入操作符“ >>” 将略去回车换行符。

char c;int n = 12;while(n){ ifs>>c; cout<<c<<endl; n--;}

文件内容:HowareyouThankyou程序运行结果:HowareyouTha

Page 56: 第 9 章  I/O 流类库

56

2. 文本文件的写 文件流的写对应内存变量的输出,即产生一个输出流。 ofstream 类的对象可以使用 ostream 类中定义的输出操作符“ <<” ,以及 put() 、wirte() 成员函数“输出”信息。例 9.7 把内存变量中的字符数据输出到当前目录下的文件 Data2.dat 中。

Page 57: 第 9 章  I/O 流类库

57

用 put() 成员函数输出void main(){ ofstream ofs("data2.txt"); if(!ofs) { cerr<<"错误:无法打开文件 "<<endl; return; } char str[]="Howareyou\nThankyou"; int len = strlen(str); for(int i = 0; i < len; i++) ofs.put(str[i]);}

程序运行后,文件 data2.txt中的数据为:Howareyouhankyou

Page 58: 第 9 章  I/O 流类库

58

用write() 成员函数输出

用输出操作符“ <<” 输出

char str1[] = "Howareyou\n";char str2[] = "Thankyou\n ";ofs.write(str1, strlen(str1));ofs.write(str2, strlen(str2));

char str1[] = "Howareyou\n";char str2[] = "Thankyou\n ";ofs<<str1;ofs<<str2;

Page 59: 第 9 章  I/O 流类库

59

9.5.3 二进制文件的读 / 写 二进制文件不仅支持顺序访问,还支持随机访问。 文本文件的读 / 写要进行字符‘ \n’与回车和换行符之间的转换,而在二进制方式下,不进行这样的转换。 除字符转换方面略有不同,二进制文件的处理过程与文本文件的处理过程基本相同。 在创建文件流类的文件对象时,用逻辑或的方法加上操作模式 ios::binary ,即表示采用二进制格式进行文件流的读 / 写。如下例:

Page 60: 第 9 章  I/O 流类库

60

// 写文件ofstream ofs("data3.dat", ios::binary);//… 判断打开成功与否int d = 44, r=99;char c =',';ofs<<d;ofs<<c;ofs<<r;ofs.close();// 读文件ifstream ifs("data3.dat", ios::binary);//…ifs>>d;ifs>>c;ifs>>r;cout<<"d = "<<d<<endl;cout<<"r = "<<r<<endl;ifs.close();

Page 61: 第 9 章  I/O 流类库

61

例 任意二进制文件的拷贝// 以二进制方式打开源文件,创建一个输入流char fileName1[20];cout<<"input source filename:";cin>>fileName1;ifstream source(fileName1, ios::nocreate||ios::binary);if(!source){ cerr<<“error: cannot open file."<<endl; return;}

Page 62: 第 9 章  I/O 流类库

62

// 以二进制方式打开目标文件,创建一个输出流 char fileName2[20];cout<<"input target filename:";cin>>fileName2;ofstream desc(fileName2, ios::binary);if(!desc){ cerr<<"error:cannot open file."<<endl; return;}

Page 63: 第 9 章  I/O 流类库

63

// 设置拷贝中介:内存变量 char ch;while(!source.eof()){ source.get(ch);desc.put(ch);}cout<<"file copy finished."<<endl;source.close();desc.close();

sourcefile targetfile内存变量

输入流 输出流

Page 64: 第 9 章  I/O 流类库

64

二进制文件支持随机访问,使用 read() 和write() 成员函数读 / 写文件。istream& istream::read(char*, int);ostream& ostream:write(const char*, int);例 9.8 P208

Page 65: 第 9 章  I/O 流类库

65

9.5.4 随机访问文件 文件指针

指示文件流的当前位置。 读指针 写指针

文件指针的定位 操作读指针的成员函数:

istream& seekg(<偏移量 >, <参照位置 >); 操作写指针的成员函数:

ostream& seekp(<偏移量 >, <参照位置 >);偏移量:文件指针相对于参照位置偏移的字节量。参照位置: ios::beg ios::cur ios::end

Page 66: 第 9 章  I/O 流类库

66

例 9.9 随机访问文件示例。 P210

Page 67: 第 9 章  I/O 流类库

67

9.6 可流类 可流类

不仅用户自定义的结构体数据类型可以重载输入输出运算符,用户自定义类中的数据成员也可通过重载输入运算符和输出运算符,用“ >>” 输入和用“ <<” 输出。 C++ 语言把可用流的输入运算符进行输入和可用输出运算符进行输出的类称作可流类。 把用户自定义类变为可流类的方法就是重载输入运算符“ >>” 和输出运算符“ <<” ,并把这两个重载函数定义为该类的友元函数。例 9.10 把复数类设计为可流复数类。重载运算符“ <<” 和“ >>” ,使用户能直接输入和输出复数。

Page 68: 第 9 章  I/O 流类库

68

#include<iostream.h>class complex{ float real, imag;public: complex() : real(0), imag(0){} complex(float r, float i):real(r), imag(i){} complex operator+(const complex &x); friend ostream & operator<<(ostream &, complex &); friend istream & operator>>(istream &, complex &);};inline complex complex::operator+(const complex &x){ return complex(real+x.real, imag+x.imag);}

Page 69: 第 9 章  I/O 流类库

69

ostream & operator<<(ostream &os, complex &obj){ os<<"("<<obj.real<<", "<<obj.imag<<")"<<endl; return os;}istream & operator>>(istream &is, complex &obj){ cout<<"Input the complex:"<<endl; is>>obj.real>>obj.imag; return is;}

Page 70: 第 9 章  I/O 流类库

70

void main(){ complex c1(2.4, 4.6), c2, c3; cout<<"the value of c1 is: "<<c1<<endl; cin>>c2; cout<<"the value of c2 is: "<<c2<<endl; c3 = c1 + c2; cout<<"the value of c3 is: "<<c3<<endl;}程序运行结果:the value of c1 is: (2.4, 4.6)Input the complex:键盘输入: 1.2 2.4the value of c2 is: (1.2 2.4)the value of c3 is: ( 3.6 7)

Page 71: 第 9 章  I/O 流类库

71

说明:1. 设计可流类时必须把重载的输入或输出运算符定义为该类的友元函数。2. 输出运算符和输入运算符重载时只能重载为友元函数,不能重载为成员函数。