c i o n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
第七章 C++ 的 I/O 流类库 PowerPoint Presentation
Download Presentation
第七章 C++ 的 I/O 流类库

Loading in 2 Seconds...

play fullscreen
1 / 54

第七章 C++ 的 I/O 流类库 - PowerPoint PPT Presentation


  • 114 Views
  • Uploaded on

第七章 C++ 的 I/O 流类库. 1. 7.1 C++ 为何建立自己的输入输出系统. C++ 除了完全支持 C 语言的输入输出系统外,还定义了一套面向对象的输入输出系统。为什么 C ++ 还要建立自己的输入输出系统呢? C 语言的输入输出系统不支持用户自定义的对象,如: struct my_struct{ int i; float f; char *str; }s; 对此结构类型 , 在 C 语言中下面的语句是不能接受的 : printf(“%my_struct”,s);

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '第七章 C++ 的 I/O 流类库' - denim


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.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
7 1 c

1

7.1 C++为何建立自己的输入输出系统

C++除了完全支持C 语言的输入输出系统外,还定义了一套面向对象的输入输出系统。为什么C ++还要建立自己的输入输出系统呢?C 语言的输入输出系统不支持用户自定义的对象,如:

struct my_struct{ int i;

float f;

char *str;

}s;

对此结构类型,在C语言中下面的语句是不能接受的:

printf(“%my_struct”,s);

因为printf( )函数只能识别系统预定义的类型,而没有办法对新的数据类型进行扩充.。用C++的输入输出系统,就可以通过重载“<<”和“>>”运算符来解以上问题。 C++的类机制允它建立一个可扩展的输入输出系统,它可以通过修改和扩展来加入用户自定义类型及相应操作。

7 2 c

2

7.2 C++的流及流类库

7.2.1 C++的流

输入输出是一种数据传递操作,它可以看作字符序列在主机与外部介质 之间的流动。流(stream)为从源(或生产者)到目的(或消费者)的数据流的引用。流具有方向性: 与输入设备(如键盘)相联系的流称为输入流;与输出设备(如屏幕)相联系的流称为输出流;与输入输出设备(如磁盘)相联系的流称为输入输出流。

C++中包 含几个预定义的流 :

标准输入流 cin 与标准输入设备相关联

标准输出流 cout 与标准输出设备相关联

非缓冲型标准出错流 cerr 与标准错误输出设备相关联(非缓冲方式)

缓冲型的标准出错流 clog 与标准错误输出设备相关联(缓冲方式)

在缺省情况下,指定的标准输出设备是显示终端,标准输入设备是键盘。

在任何情况下(有时用户 把标准输出设备定向为其它设备),指定的标准错误输出设备总是显示终端。

cerr<<“The average cannot be computed.\n”;

7 2 2

3

7.2.2 流类库

C++流类库是用继承方法建立起来的一个输入输出类库。它具有两个平行的基类:streambuf 类,ios 类。所有其它的流类都是从它们直接或间接地派生出来的。使用C++的流类库,程序中可能应包含的头文件:

iostream.h strstream.h fstream.h iomanip.h

1. streambuf 类

streambuf

派生

filebuf

strstreambuf

conbuf

2 ios

4

ios

2. ios 类

istream

fstreambase

strstreambase

ostream

ifstream

istrstream

ofstream

ostrstream

constream

ostream_withassign

istream_withassign

fstream

strstream

iostream

iostream_withassign

slide6

5

7.3输入输出的格式控制

7.3.1 用ios 类的成员函数进行格式控制

ios类中有几个成员函数可以用来对输入输出进行格式控制。

主要控制 状态标志字、域 宽、填充字符及输出精度。

1. 状态标志字

状态标志存放在数据成员 long x_flags中。ios 类 public 中定义了一个枚举,它的每个成员可以分别定义状标志字的一个位,每一位都称为一个状标志位。

slide7

6

.

这个枚举 定义 如下:

enum{ 0000000000000001

skipws =0x0001,跳过输入中的空白,可用于输入

left =0x0002,左对齐输出,可用于输出

right =0x0004,右对齐输出,可用于输出

internal =0x0008,在符号位和基指示符后填入字符,可用于输出

dec =0x0010,转换基数为十进制,可用于输入或输出

oct =0x0020,转换基数为八进制,可用于输入或输出

hex =0x0040,转换基数为十六进制,可用于输入或输出

showbase =0x0080,在输出时显示基指示符,可于输入或输出

showpoint =0x0100,在输出时显示小数点,可用于输出

uppercase =0x0200,十六进制输出时,表示制式的和表示数值的字符一律为大写 ,可用于输出

showpos =0x0400,正整数前显示”+”符号,可用于输出

scientific =0x0800,用科学表示法显示浮点数,可用于输出

fixed =0x1000,用定点形式显示浮点数,可用于输出

unitbuf =0x2000,在输出操作后立即刷新所有流,可用于输出

stdio =0x4000,在输出操作后刷新stdout 和stderr,可用于输出 };

slide8

7

2

x_flags取以上枚举值的并存,即x_flags上的每一位(长整数为16位)中的0 1相当于一个枚举值。例如 x_flags 中放 0x0011 或 17即为 0000000000010001相当于 0x0001 与 0x0010 之并 skipws(0x0001) 0000 0000 0000 0001

dec (0x0010) + 0000 0000 0001 0000

x_flags (0x0011) 0000 0000 0001 0001

slide9

8

2. ios类中用于控制输入输出格式的成员函数

函数原型 功能

long ios::setf(long flags); 设置状态标志flags

long ios::unsetf(long flags); 清除状态标志,并返回前标志

long ios::flags(); 测试状态标志

long ios::flags(long flags); 设置标志flags,并返回前标志

int ios::width(); 返回当前的宽度设置值

int ios::width(int w); 设置域宽w,返回以前的设置

int ios::precision(int p); 设置小数位数p返回以前的小数位数

char::iosfill(); 返回当前的填充字符

char::iosfill()char ch; 设置填充字符ch返回当前的填充字符

slide10

9

(1)设置状态标志

设置状态标志,即是将某一状态标志位置“1”,可使用setf()函数

其一般的调用格式为:流对象.setf(ios::状态标志);

例7.1

#include<iostream.h>

main()

{ istream cin; //对象cin可不定义

ostream cout; //对象cout可不定义

cin.setf(ios::shipws); //键盘输入时跳过输入空白

cout.setf(ios::left); //设置输出左对齐

cout.setf(ios::showpos | ios::sciengific);//中间用运算符 “|”分隔

cout<<567<<“ ” <<567.89<<endl;

}

设置showpos使得每个正数前添加“+”号, 设置sciengific使 浮点数按科学表示法

(指数形式)进行显示,输出结果:

+567 +567.89e02

注意:要设置多项标志时,中间用或运算符 “|” 分隔。

slide11

10

(2)清除状态标志

清除某一状态标志,即是将某一状态标志位置”0”,可使用unseft()函数,使用时的调用格式与setf()相同。

(3)取状态标志

取一个状态标志,可使用 flags()函数。flags()函数有不带参数与带 参数两 种形式:ling ios ::flags(); 用于返回当前的状态 标志字; ling ios ::flags (liog flag); 返回当前的状态 标志字后,再者将状态标志字设置成flag(参数)。

flags(liog flag)函数与setf(liog flags)函数的差别在于:setf()函数是在原有的基础上追加设定的,而flags()函数是用 新设定覆盖以前的状态标志字。

例 7.2几个成员函数的使用 方法

#include <iostream.h>

void showflags(long f) //输出状态标志字函数

{ long i;

for(i=0x8000;i;i=i>>1)//用右移方法使i中的值为”1”的位不断右移

if (i&f) cout<<”1”; //判断f中的某一位是否为”1”

else cout <<”0”;

cout<<endl;

}

1 7 2

11

接1 例 7.2

void main()

{ long f;

cout.setf(ios::unitbuf|ios::skipws);

f=cout.flags(); //取当前状态标志字

showflags(f); //显示状态标志字

cout.setf(ios::showpos|ios::scientific); //追加状态 标志位

f=cout.flags();

showflags(f);

cout.unsetf(ios::scientific); //从状态标志字中去掉sciengific

f=cut.flags();

showflags(f);

f=cout.flags(ios::oct); //重新设置状态标志字

showflags(f); //显示设置前的状态标志字

f=cout.flags(); //取设置 后的状态标志字

showflags(f); //显示设置后的状态标志字

}

程序运行结果为;0010000000000001 (1)0010110000000001 (2) 0010010000000001 (3)0000000000100000 (4)0000000000100000 (5)

slide13

12

(4)设置域宽

域宽主要用来控制输出,在ios 类中域 宽存放在数据成员int x_width中,设置域宽的成员函数有两个,其一般格式为:int ios ::width(); 返回当的域宽值

int ios ::width(int w); 设置域宽,并返回原来的域 宽

(5)设置输出的精度

精度用来控制浮点数的输出显示精度,在ios类中用数据成员int x_precision来存放精度,设置精度的成员函数的一般格式为: int ios::precision(int p); 重新设置浮点数所需小数的位数,并返回设置前的位数

(6)填充字符

填充字符的作用是:当输出值不满域宽时用填充字符来填充,缺省情况下填充字符为空格,所以在使用填充字符函数时,必须与width()函数相配合,否则就没有意义,在ios类中用数据成员x_fill来存放填充字符。填充字符的成员函数有两个,其一般形式为: char ios ::fill(); 返回当的填充字符

char ios ::fill(char ch); 用ch重新设置填充字符,并返回设置前的填充字符。

slide14

13

例 7.3

#include <iostream.h>

void main()

{ cout<<”x_width=”<<cout.width()<<endl;cout<<”x_fill=”<<cout.fill()<<endl;cout<<”x_precision=”<<cout.precision()<<endl;cout<<123<<” “<<123.45678<<endl;cout<<”---------------------------------------\n”;cout<<”****x_width=10,x_fill= ,x_precision=4****\n”;cout.width(10);cout.precision(4);cout<<123 <<” “<<123.45678<<” “<<234.567<<endl;cout <<”x_width=”<<cout.width()<<endl;cout<<”x_fill=”<<cout.fill()<<endl;cout<<”x_precision=”<<cout.precision()<<endl;cout<<”---------------------------------------\n”;cout<<”****x_width=10,x_fill=&,x_precidion=4****\n”;

1 7 3

14

程序运行结果如下:x_width=0x_fill=x_precision=0123 123.45678-------------------------------------

****x_width=10,x_fill= ,x_preciosion=4**** 123 123.4568 234.567x_width=0x_fill=x_precision=4-------------------------------------

****x_width=10,x_fill=&,x_precision=4****

&&&&&&&123 123.4568123&&&&&&& 123.4568x_width=0x_fill=&x_precision=4

接1 例 7.3

cout.fill(‘&’);cout.width(10);cout<<123<<” “<<123.45678<<endl;cout.setf(ios::left);cout.width(10);

cout<<123<<” “<<123.45678<<endl;cout<<”x_width=”<<cout.width()<<endl;cout<<”x_fill=”<<cout.fill()<<endl;cout<<”x_precision=“<<cout.precision()<<endl;

}

slide16

这程序建立了如下的表x sqrt(x) x^2 1 1 1

2 1.4142 4 3 1.7321 9 4 2 16 5 2.2361 25 6 2.4495 36 7 2.6458 49 8 2.8284 64 9 3 8110 3.1623 100 11 3.3166 12112 3.4641 14413 3.6056 16914 3.7417 19615 3.873 22516 4 25617 4.1231 28918 4.2426 32419 4.3598 36120 4.4721 400

15

例7.4 利用控制输入输出格式的成员函数建立对齐的数字表

#include<iostream..h>

#include<math.h>

void main()

{ double x;

cout,precision(4);

cout<<” x sqrt(x) x^2\n\n”;

for(x=1.0;x<=20.0;x++)

{ cout.width(8);

cout<<x<<’ ‘;

cout.width(8);

cout<<sqrt(x)<<’ ‘;

cout.width(8);

cout<<x*x<<’\n’;

}

}

7 3 2

16

7.3.2使用操作符进行输入输出格式控制

C++提供了另一种进行I/O格式控制的方法,这一方法使用了一种称为操纵符(也称为操纵函数)的特殊函数。在很多情况下,使用操纵符进行格式化控制比用ios格式标志和成员函数要方便。

1. C++预定义的操纵符

C++预定义的操纵符是以一个流引用作为其参数,并返回同一流的引用,因此它可以嵌入到输入输出操作的链中。操纵符可用来改变域宽,使整型数以八进制或十六进制形式输入或输出,并设置填充字符等。许多操纵符的功能类似于上面介绍的ios类成员函数的功能。C++提供的预定义操纵符如下:

( 1 ) dec 以十进制形式输入或输出整型数,可用于输入或输出 。

( 2 ) hex 以十六进制形式输入或输出整型数,可用于输入或输出。

( 3 ) oct 以八进制形式输入或输出整型数,可用于输入或输出。

( 4 ) ws 用于在输入时跳过开头的空白符,仅用于输入。

( 5 ) endl 插入一个换行符并刷新输出流,仅用于输出。

( 6 ) ends 插入一个空字符,通常用来结束一个字符串,仅用于输出。

( 7 ) flush 刷新一个输出流,仅用于输出。

( 8 ) setbase( int n )把转换基数设置为n(n的值为0,8,10,或16),n的缺省值为”0”,即以十进制形式输出。

slide18

17

( 9 ) resetiosflags (long f ) 关闭由参数 f 指定的格式标志,可用于输入或输出。

( 10 ) setiosflags (long f ) 设置由参数 f 指定的格式标志,可用于输入或输出。

( 11 ) setfill (int c ) c 为填充字符,缺省时为空格,可用于输入或输出。

( 12 ) setprecision (int n )设置数据小数部分的位数,缺省时小数的位数为6,可用输入输出。

( 13 ) setw (int n) 设置域宽为n,可用于输入或输出。

setiosflags resetiosflags

18

操纵符setiosflags( )和resetiosflags ( )中所用的格式标志

格式标志名 含 义

ios :: left 输出数据按域宽左对齐输出

ios :: right 输出数据按域宽右对齐输出

ios ::scientific 使用科学计数法表示浮点数

ios ::fixed 使用定点形式表示浮点数

ios ::dec 转换基数为十进制形式

ios ::hex 转换基数为十六进制形式

ios ::oct 转换基数为八进制形式

ios ::uppercase 十六进制形式和科学计数法输出时,表示数值的字

符 一律为大写

ios ::showbase 输出带有一个表示制式的字符(如”X”表示十六进

制,”O”表示八进制)

ios ::showpos 在正数前添加一个”+”号

ios ::showpoint 浮点输出时必须带有一个小数点

2.操纵符的使用

操纵符分为带参数的操纵符和不带参数的操纵符.通常,不带参数的操纵符在iostream.h 文件中定义,带参数的操纵符在 iomanip.h 文件中定义。

slide20

19

例7. 5 使用操纵符的输入输出格式控制

#include <iostream.h>

#include <iomanip.h>

void main ( )

{ cout <<setw (10)<<12<<567<<endl; //① cout<<123<<setiosflags(ios::scientific)<<setw(20)<<123.456789<<endl; //②

cout <<123<<setw(10)<<hex<<123<<endl; //③

cout <<123<<setw(10)<<oct(8)<<123<<endl; //④

cout <<123<<setw(10)<<setbase(0)<<123<<endl; //⑤ cout<<resetiosflags(ios::scientific)<<setprecision(4)<<123.456789<<endl;⑥ cout<<setiosflags(ios::scientific)<<setfill(‘#’)<<setw(8)<<123<<endl; // ⑦ cout<<resetiosflags(ios::scientific)<<setfill(‘$’)<<setw(8)<<456<<endl; // ⑧

}

  • 程序运行结果为:
  • 1234567 ①
      • 123 1.234567e+02 ②
      • 123 7b ③
      • 7b 173 ④
      • 173 123 ⑤
      • 123.4568 ⑥
      • 123##### ⑦
      • $$$$$456 ⑧
slide21

20

例7.6 二次方表与二次方根表的程序的程序的另一个版本

#include<iostream.h>

#include<iomanip.h>

#include<math.h>

void main ( )

{ double x;

cout<<setprecision (4);

cout << “ x sqrt (x) x^2\n\n”;

for (x=1.;x<=20.0;x++);

{

cout <<setw(8)<<x<<’’;

cout <<setw(8)<<sqrt(x)<<’’;

cout <<setw(8)<<x*x<<’n’;

}

}

slide22

21

3. 用户自定义的操纵符

C++除了提供此同预定义的操纵符外,也允许用户自定义操纵符,把程序中频繁使用的输入输出操作合并成一个操纵符。

若为输出流定义操纵符函数,则定义形式如下:

ostream &manip_name(ostream &stream)

{

//自定义代码

return stream;

}

若为输出流定义操纵符函数,则定义形式如下:

istream &manip_name(istream &stream)

{

//自定义代码

return stream;

}

slide23

22

例 7.7 自定义的操纵符

#include <iostream.h>

#include<iomanip.h>

ostream &output 1(ostream &stream)

{ stream.setf(ios::left);

stream<<setw(10)<<hex<<setfill(‘&’);

return stream;

}

void main( )

{ cout<<123<<endl;

cout<<output 1<<123<<endl;

}

程序运行结果如下:

123

7b&&&&&&&&

slide24

23

例 7.8

#include<iostream.h>

#include<iomanip.h>

istream &inputl (istream &in)

{ cin>>hex;

cout<< “Enter number using hex format:”;

return in;

}

void main ( )

{ int i;

cin>>inputl>>i;

cout <<endl;

}

以上程序中定义了一个操纵符函数input1,该函数要求输入一个十六进制数。

程序运行后,屏幕上显示:

Enter number using hex format:

提示用户输入一个十六进制数。

slide25

24

7.4用户自定义类型的输入输出

用户自定义的类型数据的输入或输出,可以通过重载运算符 “>>”和 “<<”来实现,使“>>”和 “<<”可以直接用来输入或输出类的对象。

7.4.1 重载输出运算符 “<<”

定义输出运算符”<<”重载函数的一般格式如下:

ostream &operator <<(ostream &stream, class_name obj )

{

//操作代码

return stream;

}

函数中的一个参数是对ostream 对象的引用,它可以是其它任何合法的标示符,但必须与return后面的标示符相同。第二个参数接受将被输出的对象,其中class_name是类名,obj为该类的对象名。

slide26

25

例 7.9输出运算符 “<<”重载的例子

#include <iostream.h>

class coord{

public:

int x,y;

coord ( ) {x=0; y=0;}

coord ( int I,int j) {x=I; y=j;}

};

ostream &operator <<(ostream &stream, coord ob)

{ stream <<ob.x<< “,”<<ob.y<<endl;

return stream;

}

void main ( )

{ coord a ( 5,6),b (10,34);

cout <<a<<b;

}

程序运行结果如下:

5,6

10,34

slide27

26

例 7.10 把重载输出运算符定义为类的友元函数,这样就可以访问类的私有成员

#include <iostream.h>

class coord{

int x,y;

public:

coord ( ) {x=0; y=0;}

coord ( int i,int j) {x=i; y=j;}

friend ostream &operator<<(ostream &stream, coord ob);

};

ostream &operator<<(ostream &stream, coord ob)

{ stream<<ob.x <<”,”<<ob.y<<endl;

return stream;

}

void main ( )

{ coord a ( 5,6),b (10,34);

cout <<a<<b;

}

程序运行结果如下:

5,6

10,34

7 4 2

27

7.4.2 重载输入运算符 “>>”

只是要把ostream换成istream ,把”<<”用”>>”代替,第二个参数是一个引用。

定义输入运算符”>>”重载函数的一般格式如下:

istream &operator >>(istream &stream, class_name &obj )

{

//操作代码

return stream;

}

例 7.11重载输入运算符 “>>”

#include<iostream.h>

class three_d{

int x,y,z;

public:

three_d(int a, int b, int c) {x=a; y=b; z=c;}

friend ostream &operator<<(ostream &output, three_d ob);

friend istream &operator>>(istream &input, three_d &ob);

};

1 7 11

28

接1 例 7.11

ostream &operator<<(ostream &output, three_d ob);

{ output<<ob.x<< “,”;

output<<ob.y<< “,”;

output<<ob.z<<endl;

return output ;

}

istream &operator>>(istream &input, three_d &ob);

{ cout<<”Enter x,y,z value:”;

input>>ob.x;

input>>ob.y;

input>>ob.z;

return input;

}

2 7 11

29

接2 例 7.11

程序运行结果如下:

10,20,30

Enter x,y,z value :40 50 60

40,50,60

void main ( )

{ three_d obj (10,20,30); //定义类three_d的对象obj

cout<<obj; //输出对象obj的成员值

cin>>obj; //输出对象obj的各成员值,将原值覆盖

cout<<obj; //输出对象obj的成员值(新值)

}

slide31

30

7.5文件的输入输出

C++把文件看作字符序列,即文件是由一个字符数据顺序组成的。根据数据的组织形式,文件可分为文本文件和二进制文件。文本文件又称ASCII文件,它的每个字节存放一个ASCII代码,代表一个字符。二进制文件则是把内存中的数据,按其在内存中的存储形式原样写到磁盘上存放。文件是一个字符流或二进制流,它把数据看作是一连串的字符,而不考虑记录的界限,它对文件的

存取以字符为单位进行。我们把这种文件称为流式文件。在C++中,要进行文件的输入输出,必须首先创建一个流,然后将这个流与文件相关联,即打开文件,此时才能进行读写操作,完成后再关闭这个文件。这是C++中进行文件输入输出的基本过程。

7.5.1 文件的打开与关闭

1. 文件的打开

为了执行文件的输入输出,C++提供了三个流类。

名 称 基 类 功 能

ofstream 由ostream 派生 用于文件的输出

ifstream 由istream 派生 用于文件的输入

fstream 由iostream 派生 用于文件的输入或输出

slide32

31

建立流的过程就是定义流类的对象,例如:

ifstream in;

ofstream out;

fstream both;

它们分别定义了输入流对象in,输出流对象out,输入输出流对象both。

Open( )函数是上述三个流类的成员函数,其原型为:

void open(const unsigned char *,int mode ,

int access=filebuf::openprot);

打开语句:

流类的对象. open(const unsigned char *,int mode ,int access=filebuf::openprot);

其中第一个参数是用来传递文件名的;第二个参数mode的值决定文件将如何被打开,它必须取下面的值中的一个;access的值决定文件的访问方式。

slide33

32

ios::app 使输出追加到文件尾部

ios::ate 查找文件尾

ios::in 打开一个文件进行读操作

ios::nocreate 文件不存在时,导致open( )失败

ios::noreplace 若文件存在,则open( )失败

ios::out 打开一个文件进行写操作

ios::trunc 使同名文件被删除

ios::binary 文件以二进制方式打开,缺省时为文本文件

访问方式

0 普通文件

1 只读文件

2 隐含文件

4 系统文件

8 备份文件

slide34

33

打开文件的步骤:

(1) 定义一个类的对象,例如: ofstream out;

(2) 使用open( )函数打开文件。例如:out.open(”test”,ios::out ,0);

文件只有在打开后,才能对文件进行读写操作。由于某些原因,可能打开失败,因此使用文件之前必须进行执行检测,以确认打开一个文件是否成功。可以使用类似下面的方法进行检测:

if(! mystream)

{ cout<<”cannot open file! \n”;

//错误处理代码

}

在实际编程时,打开一个文件的最常见的形式为:

ofstream out (”test”) ; 用 if (!out) 检测是否打开成功

它相当于语句:

ofstream out;

out.open(”test”);

2. 文件的关闭

关闭文件可使用close( )函数完成,close函数也是流类中的成员函数,它不带参数,没有返回值,例如: out.close ( );

7 5 2

34

7.5.2 文件的读写

在含有文件操作的程序中,必须有如下的预处理命令:

# include <fstream.h>

1. 文本文件的读写

文件打开后,文本文件的读写使用运算符“<<”与“>>”,只是必须用与文件相连接的流代替cin和cout。

例 7.12 把一个整数、一个浮点数和一个字符串写到磁盘文件test中。

# include <fstream.h>

int main ()

{ ofstream fout(”test”);

if (!fout)

{ cout<<”cannot open output file \n”;

return 1;

}

fout<<10<<” ”<<123.456<<” \”This is a test file.\”\n”;

fout.close();

return 0;

}用“写字板”可以看到test文件的内容:10 123.456 “this is a test file”

slide36

35

例 7.13 先建立一个输出文件,向它写入数据,然后关闭文件,再按输入模式打开它,并读取信息。

# include <fstream.h>

void main ()

{ ofstream fout(”test2”);

if(!fout)

{ cout<<”cannot open output file .\n”; return 1; }

fout<<” Hello!\n”;

fout<<100<<’ ’<<hex<<100<<endl;

fout.close ();

ifstream fin(”test2”);

if(!fin)

{ cout<<”cannot open input file .\n”; return 1; }

char str[80];

int I;

fin>>str>>i;

cout<<str<<” ”<<i<<endl;

fin.close();

}

程序建立一个输出文件test2,并向它写入数据,关闭输出文件test2。再将文件test2按输入模式打开,并将字符串“hello!\n”读给字符数组str,将整数100读给整型变量i。最后在屏幕上显示出str和I的值,如下所示:

Hello! 100

slide37

36

例 7.14 从键盘读入字符串,并将它们写进磁盘文件。当用户输入空白字符时,程序停止。

# include <fstream.h>

# include <stdio.h>

void main(int argc,char *argv[])

{ if(argc!=2)

{ cout<<”Usage: write <filename>\n”; return 1; }

ofstream outf (argv[1]);

if (! outf)

{ cout<<”cannot open output file .\n”; return 1; }

char str[60];

cout<<”Write strings to disk, RETURN to stop\n”;

do

{ cout<<”:”;

gets(str);

outf<<str<<endl;

}while (*str);

outf.close ();

}

这个例子采用了命令行参数的形式,输入以下命令行:

<可执行文件名> <磁盘文件名> ↙

程序运行情况如下:

Write strings to disk,RETURN to stop

:abcdefg (从键盘输入一个字符串)

:↙ (输入空行,程序停止)

程序运行后,在磁盘上生成一个名为<磁盘文件名>的文件,其内容: abcdefg

slide38

37

2. 二进制文件的读写

任何文件,无论它包含格式化的文本还是包含原始数据,都能以文本方式或二进制方式打开。文本文件是字符流,而二进制文件是字节流。

(1) 用get( )函数和put( )函数读写二进制文件

get( )是输入流类istream中定义的成员函数,它可以从与流对象连接的文件中读出数据,每次读出一个字节(字符)。put( )是输出流类ostream中的成员函数,它可以向与流对象连接的文件中写入数据,每次写入一个字节(字符)。

get( )函数有许多格式,其最一般的使用版本的原型如下:

istream &get(unsigned char &ch);

get( )函数从相关流中只读一个字符,并把该值放如引用ch&中。

put( )函数的原型如下:

ostream &put(char ch);

put( )函数将字符ch写入流中。

slide39

38

例 7.15 实现任意类型的文件拷贝。

# include <iostream.h>

# include <fstream.h>

void main (int argc, char *argv[ ])

{ char ch;

if (argc!=3)

{ cout<<”Bad commond !\n”; return 0; }

ifstream inf (argv[1]);

if (! inf)

{ cout<<”Cannot open souce file.”; return 1; }

ofstream outf (argv[2]);

if (! outf)

{ cout<<”Cannot open target file.”; return 1; }

while (inf)

{ inf.get (ch);

outf.put (ch);

}

inf.close ( );

outf.close ( );

}

假定本程序的文件名为mvc7_15.cpp,经编译、连接后生成的可执行文件名为mvc7_15.exe,则在DOS下输入以下命令行:

D> mvc7_15 file1.exe file2.exe

该程序的执行结果,是把文件file1.exe中的信息拷贝到文件file2.exe中

slide40

39

eof( )函数的使用

二进制文件的处理过程与文本文件的处理过程基本相同,但在判断文件是否结束时有所区别。在文本文件中,遇到文件结束符,get函数返回一个文件结束标志EOF,该标志的值为-1。但在处理二进制文件时,读入某一字节中的二进制数的值可能是-1,这与EOF的值相同。这样就有可能出现读入的有用数据被处理成“文件结束”的情况。为了解决这个问题,c++提供了一个成员函数eof,用来判断文件是否真的结束,其原型为:

int eof();

当到达文件末尾时,它返回一个非零值,否则返回零。当输入文件是键盘时,其结束符是ctrl_z, eof( ) 函数返回的值为真。

slide41

40

例 7.16在屏幕上显示任何给定文件的内容。

# include <iostream.h>

# include <fstream.h>

main (int argc, char *argv[ ])

{ char ch;

if (argc!=2)

{ cout<<”Usage: PR<filename>\n”; return 1; }

ifstream in (argv[1]);

if (! in)

{ cout<<”Cannot open file”; return 1; }

while (! in.eof ( ))

{ in.get (ch);

cout<<ch;

}

return 0;

}

假定本程序的文件名为mvc7_16.cpp,经编译、连接后生成的可执行文件名为mvc7_16.exe,则在DOS下输入以下命令行:

D> mvc7_16 file1

该程序的执行结果,是把文件 file1 中的信息在屏幕上显示。

2 read write

41

(2) 用read( )函数和write( )函数读写二进制文件

有时需要读写一组数据 (如一个结构变量的值),为此C++提供了两个函数read( )和write( ),用来读写一个数据块。

① read( )函数:

函数原型: istream &read (unsigned char *buf,int num);

read( )是类istream中的成员函数,其功能为:从相应的流中读取num个字节(字符),并把它们放入指针buf所指的缓冲区中。

调用格式: read(缓冲区首址,读入的字节数);

注意:“缓冲区首址”的数据类型为unsigned char*,当输入其它类型数据时,必须进行类型转换,例如: int array[ ]={50,60,70};定义了一个整型数组array,

read((unsigned char *)&array,sizeof(array);

对整型数组array,为了把数据从文件读入到它中,必须在read( )函数中把它转换为unsigned char *类型。由sizeof( )函数确定要读入的字节数。

slide43

42

② write( )函数:

函数原型: ostream &write(const unsigned char *buf, int num);

write( )是流类ostream的成员函数,利用该函数,可以把buf所指的缓冲区num个字节写到相应的流上。参数的含义及调用注意事项与read()函数类似。

注意:如果在num个字节被读出之前就达到了文件尾,则read( )只是停止执行,此时缓冲区包含这些字符。我们可以用另一个成员函数gcount( )统计出有多少字符被读出。gcount( )的原型如下: int gcount( );它返回所读取的字节数。

7 17 write test

43

例 7.17 用write( )函数向文件test中写入双精度数与字符串。

# include <iostream.h>

# include <fstream.h>

# include <string.h>

main( )

{ ofstream outf(”test”);

if (! out)

{ cout<<”cannot open output file .\n”; return 1; }

double num=100.45;

char str [ ]=”This is a test”;

out.write ((char * )&num,sizeof (double ));

out.write (str, strlen (str));

out.close ( );

return 0;

} 程序执行后,屏幕上不显示任何信息,但程序已将双精度100.45和字符串“this is a test” 以二进制形式写入文件test中。用下面的程序 可以读取文件test中的数据,并在屏幕上显示出来,以验证前面的程序操作。

7 18 read 7 17

44

例 7.18 用read( ) 函数读取例7.17中程序所建立的文件。

# include <iostream.h>

# include <fstream.h>

# include <string.h>

main()

{ ifstream in(”test”);

if (! in) { cout<<”cannot open input file .\n”; return 1; }

double num;

char str [80];

in.read ((char * )&num, sizeof (double));

in.read (str, 14);

cout<<num<<’ ’<<str;

in.close ( );

return 0;

}

程序运行结果为:

100.45 This is a test

slide46

45

3. 文件的随机读写

前面介绍的文件操作都是按一定顺序进行读写的,因此称为顺序文件。

C++在类istream及类ostream中定义了几个与在输出流中随机移动文件指针的成员函数,则可以在流内随机移动文件指针,从而可以对文件的任意数据进行随机读写。用函数seekg( )和seekp( )进行随机访问。函数seekg( )用于输入文件,函数seekp( )用于输出文件。

slide47

46

它们的函数原型如下:

istream &seekg (streamoff offset, seek_dir origin);

ostream &seekp (streamoff offset, seek_dir origin);

其中,参数origin表示文件指针的起始位置,offset表示相对于这个起始位置的位移量。seek_dir是一个系统定义的枚举名,origin是枚举变量。origin的取值有以下三种情况:

ios::beg从文件头开始,把文件指针移动由offset指定的距离。offset的值为正数

ios::cur从当前位置开始,把文件指针向前(后)移动由offset指定的距离。 offset 的值为正或负数,正数时向前移,负数时向后移。

ios::end从文件尾开始,把文件指针移动由offset指定的距离,offset的值为负数进行文件的随机读写时,可以用以下函数测试文件指针的当前位置:

streampos tellg( );用于输入文件。 streampos tellp( );用于输出文件。

streampos是在iostream.h中定义的类型,实际上是long型的。

7 19 seekp
例 7.19 演示seekp( )函数。它修改文件中的指定字符。执行时在命令行指定文件名,其后跟想修改的字符的位置,然后再跟新的字符。

47

# include <iostream.h>

# include <fstream.h>

# include <stdlib.h>

int main (int argc, char *argv[ ])

{ if (argc!=4)

{ cout<<”Usage: CHANGE<filename> <byte> <char>\n”; return 1; }

fstream out (argv[1], ios::in|ios::out);

if (! out)

{ cout<<”Cannot open file”<<argv[1]<<”\n”; return 1; }

out.seekp (atoi (argv[2]), ios::beg);

out.put ( *argv[3]);

out.close ( );

return 0;

}

假定上述程序的文件名为mvc7_19.cpp,经编译、连接生成的可执行文件名为mvc7_19.exe。需要修改的文件名为file,file的内容为: AAAAAAAAAA1234567890

执行下述命令:

D> mvc7_19 file 5 #

程序运行结果为:

AAAAA#AAAA1234567890

7 20 seekg

42

48

例 7.20 使用seekg( )函数,它从指定的位置开始显示文件内容。

# include <iostream.h>

#include <fstream.h>

# include <stdlib.h>

void main (int argc, char * argv[ ])

{ char ch;

if (argc!=3 )

{ cout<<”Usage: LOCATE<filename><loc>\n”; return 1; }

ifstream inf (argv[1] );

if (! inf)

{ cout<<”Cannot open input file. \n”; return 1; }

inf.seekg (atoi (argv[2]), ios::beg);

while (! inf.eof ( ))

{ inf.get (ch);

cout<<ch;

}

inf.close ( );

}

假定程序的执行文件文件名为mvc7_20.exe,要显示文件名为file,希望从第5个位置开始显示,则执行的命令行:

D>mvc7_20 file 5 <cr>

程序运行结果为:

#AAAA1234567890

slide50

49

7.6 应 用 举 例

例7.21 重载运算符“<<”和“>>”,使用户能直接输入和输出复数。

#include<iostream.h>

class complex{

double real,imag;

public:

complex(double r, double i) { real=r; imag=i;}

complex( ) { real=0; imag=0;}

friend complex operator+(complex, complex);

friend ostream &operator<<(ostream&, complex&);

friend istream &operator>>(istream&, complex&);

};

complex operator+(complex a, complex b)//定义重载运算符“+”

{ complex temp;

temp.real=a.real+b.real;

temp.imag=a.imag+b.imag;

return temp;

}

1 7 21

50

接1 例7.21

ostream &operator<<(ostream &output, complex &obj) //定义重载运算符“<<”

{ output<<obj,real;

if(obj.imag>0) output<<”+”;

if(obj.imag!=0) output<<obj.imag<<“i”;

return output;

}

istream &operator>>(istream &input, complex &obj) //定义重载运算符“>>”

{ cout<<”Input the real, imag of the complex:\n”;

input>>obj.real;

input>>obj.imag;

return input;

}

slide52

51

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.6i

Input the real, imag of the complelx:

若此时输入:

3.7 2.5

屏幕上又显示出:

the value of c2 is 3.7+2.5i

the value of c3 is 6.1+7.1i

7 22 triangle

52

例7.22 下面的程序建立了类triangle,用来存储直角三角形的宽与高。这个类的重载输出运算符函数在屏幕上显示三角形。

# include<iostream.h>

class triangle{

int height,base;

public:

triangle (int h, int b) { height=h; base=b; }

friend ostream &operator << (ostream &stream, triangle ob);

};

ostream &operator<<(ostream &stream, triangle ob)

{ int I,,j,h,k;

i=j=ob.base-1;

for (h=ob.height-1;h;h--)

{ for (k=i;k;k--)

stream<<’ ’;

stream<<’*’;

1 7 22

53

接1 例7.22

if (j!=i)

{ for(k=j-i-1;k;k--)

stream<<’ ’;

stream<<’*’;

}

i--;

stream<<endl;

}

for (k=0;k<ob.base;k++) stream<<’*’;

stream<<endl;

return stream;

}

void main ()

{ triangle t1(5,5),t2(10,10),t3(12,12);

cout<<t1<<endl;

cout<<t2<<endl;

cout<<t3<<endl;

}