第 11 章 c++ 语言的 i/o 流类库

69
1 第 11 第 C++ 第第第 I/O 第第第 第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第第 第 C++ 第第第第第第第第C++ 第第第第第第第第 / 第第第第 第第第第第第第第第第第 第 ,一 I /O 第第第 第第第第第第第第第 C++ 第第第第第第第第第第第第 第第第 第第第第第第第第第第第第第第第第“第”第第第第第第 一一传 / 第第第第第第第第 / 第第第第第第第第 第第 第第第第第第 第第第第第第第第第第第第第第第第第第第第第 第第第第第第第第第 ,。

Upload: enoch

Post on 07-Jan-2016

100 views

Category:

Documents


0 download

DESCRIPTION

第 11 章 C++ 语言的 I/O 流类库. 本章的最主要目的就是把对象保存到磁盘文件中并从磁盘文件重建对象。当然 C++ 可完成更多的任务。 C++ 语言中并没有输入 / 输出语句,而是在标准库里包含了一个 I/O 流类库,它与标准模板库同为 C++ 标准库中最重要的组成部分。数据从一个对象到另一个对象的传送被抽象为“流”。数据的输入 / 输出就是通过输入 / 输出流来实现的。   流是一种抽象的概念,负责在数据的产生者和数据的使用者之间建立联系,并管理数据的流动。. 第 11 章 C++ 语言的 I/O 流类库. 11.1 I/O 流类库的功能. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第 11 章   C++ 语言的 I/O 流类库

1

第 11 章 C++ 语言的 I/O 流类库

 本章的最主要目的就是把对象保存到磁盘文件中并从磁盘文件重建对象。当然 C++ 可完成更多的任务。

   C++ 语言中并没有输入 / 输出语句,而是在标准库里包含了一个 I/O 流类库,它与标准模板库同为 C++ 标准库中最重要的组成部分。数据从一个对象到另一个对象的传送被抽象为“流”。数据的输入 / 输出就是通过输入 / 输出流来实现的。

  流是一种抽象的概念,负责在数据的产生者和数据的使用者之间建立联系,并管理数据的流动。

Page 2: 第 11 章   C++ 语言的 I/O 流类库

2

第 11 章 C++ 语言的 I/O 流类库

11.1 I/O 流类库的功能

11.3 格式化输入输出操作

11.2 常用的读写操作

11.4 插入符和提取符的重载

11.7 流错误处理

11.5 磁盘文件的输入和输出操作

11.6 字符串流操作

Page 3: 第 11 章   C++ 语言的 I/O 流类库

3

11.1 I/O 流类库的功能  整个流类体系是一个派生类体系。按 ANSI C++标准,类 ios 是抽象类,它的析构函数是虚函数,它的构造函数为保护的,作为所有基本流类的基类。 VC++ 中有一个构造函数 ios (streambuf*) 为公有,与ANSI C++ 不同。

Page 4: 第 11 章   C++ 语言的 I/O 流类库

4

ios

istream ostream

ifstream iostream oftream

strstream

istrstream ostrstream

fstream

Page 5: 第 11 章   C++ 语言的 I/O 流类库

5

11.1 I/O 流类库的功能11.1.1 基本的 I/O 流类库

  在流类库中,最重要的两部分功能为标准输入 /输出( standard input/output )和文件处理。 在 C++ 的流类库中定义了四个全局流对象: cin ,cout , cerr 和 clog 。可以完成人机交互的功能。 cin 标准输入流对象,键盘为其对应的标准设备。 cout 标准输出流对象,显示器为标准设备。 cerr 和 clog 标准错误输出流,输出设备是显示器。 其中 cin 、 cout 和 clog 是带缓冲区的,缓冲区由 streambuf 类对象来管理。而 cerr 为非缓冲区流,一旦错误发生立即显示。 要使用这四个功能,必须包含 <iostream.h> 文件。

Page 6: 第 11 章   C++ 语言的 I/O 流类库

6

11.1.2 文件 I/O 操作流类库类 ifstream 由类 istream 单一派生,专门用来处理

文件的输入操作。类 ofstream 由类 ostream 单一派生,专门用来处

理文件的输出操作。类 fstream 由类 iostream 单一派生,专门用来处

理文件的输入和输出操作。类 ifstream 、 ofstream 、 fstream 拥有公共基类:

fstreambase 。利用上述类创建流对象,类中的成员函数 open 用

来打开文件,成员函数 close 用来关闭文件,有关细节将在 11.5 节中讲述。

Page 7: 第 11 章   C++ 语言的 I/O 流类库

7

11.1.3 字符串流操作流类库类 istrstream 由类 istream 单一派生,专

门用来处理字符串的输入操作。类 ostrstream 由类 ostream 单一派生,

专门用来处理字符串的输出操作。类 strstream 由类 iostream 单一派生,

专门用来处理字符串的输入和输出操作。类 istrstream 、 ostrstream 、 strstrea

m 拥有公共基类: strstreambase 。有关字符串流操作的细节将在 11.6 节中

讲述。

Page 8: 第 11 章   C++ 语言的 I/O 流类库

8

11.2 常用的读写操作 11.2.1 屏幕输出操作 1. 使用预定义的插入符 << << 是左移位运算符的重载,格式为: 字符流目的地 << 字符流内容 << 重载函数是 ostream 类的成员函数,原型为: ostream & operator<<(const char* psource); ostream & operator<<(char source); ostream & operator<<(int source); … 需在屏幕显示时,屏幕就是字符流目的地,头文件 iostrea

m.h 中定义有 ostream 类的全局对象 cout 就代表屏幕。

Page 9: 第 11 章   C++ 语言的 I/O 流类库

9

使用 << 时应注意运算符的优先级别,必要时,用加括弧的方法保证其他运算符优先运算,例如:

cout<<(i>j?i:j)<<endl; 如果不加 (), 将因运算顺序不对而编译出

错。

Page 10: 第 11 章   C++ 语言的 I/O 流类库

10

【例 11.1 】插入符用法示例

#include<iostream.h>#include<string.h>void main( ){ cout<<" 字符串 \"This is a string.\" 的长度为: "; cout<<strlen("This is a string.")<<endl; cout<<" 字符串 \"This is a string.\" 占的空间为: "; cout<<sizeof("This is a string.")<<endl;}运行结果为 17 和 18, 因为 strlen 是求字符串的长度,即

字符串中字符的个数; sizeof 是求占用内存的字节数,其中包括字符串结束标志空字符占用的内存。 strlen在 string.h 中定义, sizeof 是一个运算符。

Page 11: 第 11 章   C++ 语言的 I/O 流类库

11

【例 11.2 】输出变量地址值示例

#include<iostream.h>void main( ){ int x(100); int *px=&x; cout<<"x="<<x<<"\n 十进制 &x="<<long(&x)<<endl; cout<<"*px="<<*px<<"\n 十进制 px="<<long(px)<<endl; cout<<"*px="<<*px<<"\n 十进制 &px="<<long(&px)<<en

dl; cout<<"*px="<<*px<<"\n 十六进制 &px="<<&px<<endl;}运行结果显示直接输出地址值时是十六进制,若要以十进

制显示需做强制类型转换为长整型。

Page 12: 第 11 章   C++ 语言的 I/O 流类库

12

【例 11.3 】编写判断除数为 0 的整数除法程序

#include<iostream.h>void fun(int i,int j){ if(j)cout<<" 商为: "<<i/j<<endl; else cout<<" 除数为 0, 不能求商! \n";}void main( ){ int x,y; for(int i=0;i<3;i++) { cout<<" 输入被除数和除数: ";cin>>x>>y; fun(x,y); } }请仔细阅读本程序,并与课本上的程序比较。

Page 13: 第 11 章   C++ 语言的 I/O 流类库

13

2. 使用成员函数 put( ) 输出一个字符put 函数函数原型为: ostream& put(char c);使用 put 函数向屏幕输出字符的格式为:cout.put( 字符 )

【例 11.4 】用成员函数 put( ) 输出一个字符的例子#include<iostream.h>void main( ){ cout<<'B'<<'E'<<'I'<<'J'<<'I'<<'N'<<'G'<<'\n'; cout.put('B').put('E').put('I').put('J').put('I').put('N').put('G').put

('\n'); char c1=65,c2=66,c3=67; cout.put(c1).put(c2).put(c3).put('\n');} 请仔细阅读本程序,注意函数 put() 的值是 ostream 类对象

的引用,连续输出时连续加 .put()

Page 14: 第 11 章   C++ 语言的 I/O 流类库

14

3. 使用成员函数 write( ) 输出一个字符串write 函数函数原型为:ostream& write(const char *str,int n);使用 write 函数向屏幕输出字符的格式为:cout.write( 字符指针 , 字符个数 )

【例 11.5 】用成员函数 write( ) 输出一个字符串的例子#include<iostream.h>#include<string.h>void print(char*s){cout.write(s,strlen(s)).put('\n');cout.write(s,6)<<'\n';}void main( ){ char*str=" 我爱北京天门! "; cout<<" 字符串为: \""<<str<<"\"\n"; print(str);} 请仔细阅读本程序,注意函数 write( ) 的值是 ostream 类

对象的引用,连续输出时连续加 .write( )

Page 15: 第 11 章   C++ 语言的 I/O 流类库

15

11.2.2 键盘输入操作

1. 使用预定义的提取符 >>>> 是右移位运算符的重载,格式为: 输入流源头 >> 输入流目的地 >> 重载函数是 istream 类的成员函

数,原型为: istream & operator>>(int &); istream & operator>>(char &); istream & operator>>(char *); …

Page 16: 第 11 章   C++ 语言的 I/O 流类库

16

需从键盘接收数据时,键盘就是输入流源头,输入流目的地应是变量名,头文件 iostream.h 中定义有 istream 类的全局对象 cin 就代表键盘。

提取符 >> 依次从输入源中提取一个数据项,连续提取时,数据项之间用空格、制表符、换行等空白符分隔。

如对语句 cin>>x>>y>>z; 可键入: 18 19 20< 回车 >

Page 17: 第 11 章   C++ 语言的 I/O 流类库

17

1. 使用预定义的提取符 >>

标准设备 ( 键盘 ) 输入是最不安全的,注意以下几点,可以避免错误:① cin 为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲

区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据。不可能用刷新来清除缓冲区,所以不能输错,也不能多输!

② 输入的数据类型必须与要提取的数据类型一致,否则出错,出错只是在流的状态字 state(枚举类型 io_state)中对应位置位(置 1),程序继续。所以要提高健壮性,就必须在编程中加入对状态字 state 的判断。

③ 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为 32)无法用 cin 输入,字符串中也不能有空格。回车符也无法读入。

Page 18: 第 11 章   C++ 语言的 I/O 流类库

18

【例 11.6 】提取符用法示例

#include<iostream.h>void main( ){ int x,y; cout<<" 请输入两个整数: "; cin>>x>>y; cout<<'('<<x<<','<<y<<")\n";}

Page 19: 第 11 章   C++ 语言的 I/O 流类库

19

2. 使用成员函数 get( ) 输入一个字符串get 函数函数原型有多个,其中比较简单的 2 个是:int get( ); 、 istream& get(char& c);前者提取的字符作为函数值,后者提取的字符放在参数 c使用 get 函数从键盘提取字符的格式分别为:cin.get( ) 、 cin.get( 字符变量 )

【例 11.7 】用成员函数 get( ) 从键盘提取字符的例子

#include<iostream.h>void main( ){ char ch; cout<<" 输入字符串: "; while((ch=cin.get( ))!=EOF)cout.put(ch);}

Page 20: 第 11 章   C++ 语言的 I/O 流类库

20

#include<iostream.h>void main( ){ char ch1,ch2; cout<<" 输入字符串: "; while(cin.get(ch1).get(ch2)) cout.put(ch2).put(ch1);}

请仔细阅读本程序,注意这种格式的函数 get( ) 的值是 istream 类对象的引用,连续输入时连续加 .get( )

【例 11.8 】使用成员函数 get带参数的形式输入一个字符

Page 21: 第 11 章   C++ 语言的 I/O 流类库

21

cin.get(ch)与 cin.get()

特征 cin.get(ch) ch=cin.get()

传输输入字符的方法 赋给参数 ch 将函数返回值赋给 ch

字符输入时函数的返回值

istream 类对象的引用

字符编码( int 值)

达到文件尾时函数的返回值

转换为 false EOF

Page 22: 第 11 章   C++ 语言的 I/O 流类库

22

#include<iostream.h>const int N=40;void main( ){ char s1[N],s2[N]; cout<<" 输入一个字符串: ";cin>>s1;// 输入时遇空格、制表符、回车等结束 ,但这些结束符留在缓冲区中 cout<<"s1="<<s1<<endl; cin.get(s2,N);// 从缓冲区中最多接收 N-1 个字符 ,放数组 s2 中 ,界定符为隐含换行符 cout<<"s2="<<s2<<endl;} 请仔细阅读本程序,注意 get 函数的第三种原型:istream &get(char *c,int n,char ch='\n'); 这种格式的函数 get( ) 的值是 istream 类对象的引用,使用格式为: cin.get( 字

符指针 , 整数 n,界定符 )该函数直接从缓冲区中最多读取 n-1 个字符数据,若前面遗留有换行,则结束。

【例 11.9 】混合使用成员函数 get 、提取符输入字符,比较区别。

Page 23: 第 11 章   C++ 语言的 I/O 流类库

23

3. 使用成员函数 getline( ) 输入一行字符getline 函数原型是:istream &getline(char *buf,int n,char ch=‘\n’);功能是最多提取 n-1 个字符放在 buf为首地址的缓冲区中,参数 ch 为终止符,隐含的终止符为换行符。使用 getline 函数从键盘提取字符串的格式为:cin.getline( 字符指针 , 整数 n,终止界定符 )输入字符少于 n-1 时,接收实际输入字符数,并将缓冲区的回车符清除,若输入字符数不少于 n-1 时,多余的字符及回车符留在缓冲区。该函数终止读取字符的条件为:①读取 n-1 个字符②遇到参数指定的终止符③遇到文件结束符或其他输入流的结束符。

Page 24: 第 11 章   C++ 语言的 I/O 流类库

24

【例 11.10 】输入若干行字符,求出最长行的字符个数及输入行数。使用成员函数 getline( )

#include<iostream.h>const int SIZE=80;void main( ){ char buf[SIZE]; int lcnt=0,lmax=-1; cout<<" 输入若干行字符: "; while(cin.getline(buf,SIZE))// 用 Ctrl+Z 作输入流结束标志 {int count=cin.gcount( );//gcount计算刚刚用 getline 函数输入的字符数的成员函数 lcnt++;if(count>lmax)lmax=count; cout<<"line#"<<lcnt<<':'<<count<<endl; cout.write(buf,count).put('\n').put('\n'); } cout<<endl; cout<<"最长行字符数 :"<<lmax<<endl; cout<<" 总行数 :"<<lcnt<<endl; }

Page 25: 第 11 章   C++ 语言的 I/O 流类库

25

4. 使用成员函数 read( ) 输入一行字符read 函数原型是:istream &read(char *buf,int n);功能是最多提取 n-1 个字符放在 buf 为首地址的缓冲区中使用 read 函数从键盘提取字符串的格式为:cin. read( 字符指针 , 整数 n)最多接收 n 个字符,接受字符中可以包括换行符 ('\n') 。该函数终止读取字符的条件为:①读取 n 个字符②遇到文件结束符 (Ctrl+Z) 。

Page 26: 第 11 章   C++ 语言的 I/O 流类库

26

【例 11.11 】输入若干行字符,并输出。使用成员函 read( ) 。

#include<iostream.h>void main( ){ const int SIZE=80; char buf[SIZE]=""; cout<<"输入若干行字符 :\n"; cin.read(buf,SIZE); cout<<endl; cout<<buf<<endl;}

Page 27: 第 11 章   C++ 语言的 I/O 流类库

27

11.3 格式化输入输出操作

1.控制格式的标志位定义为公有的无名的枚举类型:enum{ skipws=0x0001, //跳过输入中的空白字符 left=0x0002, // 输出左对齐 right=0x0004, // 输出右对齐 internal=0x0008, // 在输出符号或数字字符后填充 dec=0x0010, // 在输入输出时将数据按十进制处理 oct=0x0020, // 在输入输出时将数据按八进制处理 hex=0x0040, // 在输入输出时将数据按十六进制处理 showbase=0x0080, // 在输出时带有表示数制基的字符

C++ 在类 ios 中提供格式化输入输出,有两种方法控制输入输出格式:一是使用标志位和成员函数,二是使用控制符。11.3.1 使用成员函数进行格式输出

Page 28: 第 11 章   C++ 语言的 I/O 流类库

28

11.3 格式化输入输出操作

showpoint=0x0100, // 输出浮点数时 ,必定带小数点 uppercase=0x0200, // 输出十六进制 , 用大写字母 showpos=0x0400, // 输出正数时 , 加” +”号 scientific=0x0800, //科学数方式输出浮点数 fixed=0x1000, // 定点数方式输出浮点数 unitbuf=0x2000, // 插入后 , 立即刷新流 stdio=0x4000} // 插入后 , 立即刷新 stdout 和 stderr该枚举量说明中每一个枚举量实际对应两字节数据( 16 位)中的一个位,所以可以同时采用几个格式控制,只要把对应位置 1 即可,这样既方便又节约内存。取多种控制时,用或“ |” 运算符来合成,合成为一个长整型数,在 ios 中为:protected:

long x_flags;这个长整型数据成员被称为标志字。

Page 29: 第 11 章   C++ 语言的 I/O 流类库

29

11.3 输入输出的格式控制

类 ios 中还设置了三个输入输出流格式控制标志: protected: int x_precision; // 标志浮点数精度 ,缺省为 6 位 int x_width; // 输出域宽 ,缺省域宽为 0, // 重设域宽只对其后第一输出项有效 ,如域宽不足 ,则不受限制 char x_fill; // 标志域宽有富余时填入的字符

2. 设置标志字的成员函数①long flags( ) 返回标志字函数。②long flags(long) 函数值为更新前的标志字,参数作更新后的标志字

Page 30: 第 11 章   C++ 语言的 I/O 流类库

30

③long setf(long setbits,long field)该函数先将第二个参数 ( 转换为二进制 ) 中为 1 的标志位置 0 ,再将第一个参数 ( 转换为二进制 ) 中为 1的标志位置 1 。函数值为设置前标志字。④long setf(long)设置参数 ( 转换为二进制 ) 中为 1 的标志位置 1, 其余标志位不变。函数值为设置前的标志字。⑤long unsetf(long)设置参数 ( 转换为二进制 ) 中为 1 的标志位置 0, 其余标志位不变。函数值为设置前的标志字。

Page 31: 第 11 章   C++ 语言的 I/O 流类库

31

在 ios 中定义有如下静态:

①static const long adjustfield;其值 =left|right|internal=0x2|0x4|0x8=0xe②static const long basefield;其值 =dec|oct|hex=0x10|0x20|0x40=0x70③static const long floatfield;其值 =scientific|fixed=0x800|0x1000=0x1800实际上,上述 3 个组合设置是有矛盾的,如①中又

要左对齐,又要右对齐是不可能的,自己可以编程测试实际结果,上述组合常用于清除标志位。

例如,若需清除数制标志后,再设置十六进制标志,可调用成员函数:

cout.setf(ios::hex,ios::basefield)

Page 32: 第 11 章   C++ 语言的 I/O 流类库

32

3. 控制输出格式的成员函数

ios 类中有如下控制输出格式的成员函数:①int width( ); int width(int);

前者函数值为当前输出的数据宽度;后者参数为设置的数据输出宽度,函数值为设置前的数据输出宽度值;默认为实际需要的最少字符数,设置宽度小于默认值时,设置无效。

②char fill( ); char fill(char);

前者函数值为当前使用的填充字符;后者参数为设置的填充字符,函数值为设置前使用的填充字符;默认填充字符为空格。

③int precision( ); int precision(int);

前者函数值为当前使用的浮点数有效数字个数;后者参数为设置的浮点数有效数字个数,函数值为设置前使用的浮点数有效数字个数;单精度最多 7 位 (默认 6 位 ) 有效数字,双精度最多 15 位 (默认 6 位 )有效数字,长双精度最多 19 位 (默认 6 位 ) 有效数字。

Page 33: 第 11 章   C++ 语言的 I/O 流类库

33

【例 11.12 】设置清除标志位的例子

#include<iostream.h>void main( ){ int a=1234;double b=12.345678; cout<<cout.flags( )<<endl; cout.setf(ios::hex,ios::basefield); cout<<cout.flags( )<<endl; cout<<a<<endl; cout.setf(ios::showbase);cout<<a<<endl; cout.setf(ios::uppercase);cout<<a<<endl; cout.setf(ios::oct,ios::basefield);cout<<a<<endl; long oldflag=cout.setf(ios::dec,ios::basefield); cout<<a<<endl; cout.setf(ios::showpos); cout<<a<<endl; cout.flags(oldflag); cout<<a<<endl; cout<<cout.flags( )<<endl;cout<<b<<endl; cout.setf(ios::scientific,ios::floatfield);cout<<b<<endl; cout<<cout.flags( )<<endl; }// 运行程序,观察结果。

ios::showbase| ios::uppercase| ios::oct

即 0x2A0或 01240

ios::showbase| ios::uppercase| ios::oct| ios::scientific

即 0xAA0或 05240

Page 34: 第 11 章   C++ 语言的 I/O 流类库

34

【例 11.13 】设置控制小数位数的例子

#include<iostream.h>void main( ){ double d1=12.34567,d2=12.000; cout<<d1<<endl<<d2<<endl; cout.setf(ios::showpoint); cout<<d1<<endl<<d2<<endl;}运行程序,观察结果 , 注意:①默认的是可以不带小数点及小数部分的 0②默认的有效数字是 6 位。

Page 35: 第 11 章   C++ 语言的 I/O 流类库

35

【例 11.14 】设置输出对齐格式的例子#include<iostream.h>void main( ){ cout<<"12345678901234567890\n"; int i=12345; cout<<i<<endl; cout.width(10); cout.fill('#'); cout.setf(ios::left,ios::adjustfield ); cout<<i<<endl; cout.setf(ios::right,ios::adjustfield ); cout<<i<<endl; cout.precision(6); double d=123.456789; cout<<d<<endl; cout.setf(ios::scientific,ios::floatfield);cout<<d<<endl; cout<<" 宽度 :"<<cout.width( );}运行程序,观察结果,注意①宽度设置一次有效,再次使用需重新设置,②设置有冲突时会怎模样?怎样研究这个问题?

Page 36: 第 11 章   C++ 语言的 I/O 流类库

36

11.3.2 使用控制符进行格式输出

控制符又称操作子,是定义在头文件 iomanip.h 中的对象,可以直接插入到数据流中,被 << 和 >> 操作。

下表列出了 VC++ 支持的操作子:

Page 37: 第 11 章   C++ 语言的 I/O 流类库

37

操作符 含义 输入 /输出

dec 以十进制显示 (默认设置 ) I/O

hex 以十六进制显示 I/O

oct 以八进制显示 I/O

setbase(int n) 设置数制基数为 n(=0,8,10,16),0表示默认 (十进制 ) I/O

ws 提取空白字符 I

ends 插入空字符 O

endl 插入换行符,然后刷新 ostream 缓冲区 O

flush 刷新 ostream 缓冲区 O

resetiosflags(long)

清除参数所指定的标志位 I/O

setiosflags(long)

设置参数所指定的标志位 I/O

setfill(char) 设置填充字符 O

setprecision(int)

设置浮点数输出的有效数字位数 O

setw(int) 设置输出数据项的域宽 O

Page 38: 第 11 章   C++ 语言的 I/O 流类库

38

【例 11.15 】按整数的不同基数设置输出格式的例子

#include<iostream.h>

#include<iomanip.h>

void main( )

{ int n;

cout<<" 输入一个十进制整数: ";cin>>n;

cout<<n<<" 的十六进制形式为 :"<<hex<<n<<endl;

cout<<dec<<n<<" 的八进制形式为 :"<<oct<<n<<endl;

cout<<dec<<n<<" 的十进制形式为 :"<<n<<endl;

}

运行程序,观察结果,注意进位制基数设置后永久有效,要改变必须重新设置。

Page 39: 第 11 章   C++ 语言的 I/O 流类库

39

【例 11.16 】设置浮点数精度和填充符的例子#include<iostream.h>#include<iomanip.h>#include<math.h>void main( ){ double n=sqrt(3); for(int i=0;i<10;i++) { cout<<setprecision(i)<<setw(12)<<setfill('*'); cout<<n<<endl; }}运行程序,观察结果,注意输出宽度、输出精度 ( 有效数字

位数 ) 、填充字符等设置方法。

Page 40: 第 11 章   C++ 语言的 I/O 流类库

40

【例 11.17 】利用设置填充符、宽度的方法输出菱形#include<iostream.h>#include<iomanip.h>void main( ){ for(int i=7;i>1;i--)

{ cout<<setfill(' ')<<setw(i)<<' '<<setfill('*');cout<<setw(15-2*i)<<'*'<<endl; }

for(i=1;i<8;i++) { cout<<setfill(' ')<<setw(i)<<' '<<setfill('*');

cout<<setw(15-2*i)<<'*'<<endl; } }运行程序 ,观察结果 , 注意打印图形的算法研究 ,找各行字符个数的规律 , 注意与【例 4.16 】比较。

Page 41: 第 11 章   C++ 语言的 I/O 流类库

41

11.4 插入符 (<<)与提取符 (>>) 的重载

为了对类的对象进行插入及提取操作,可以编写重载函数实现插入符、提取符的重载。

【例 11.18 】重载提取符和插入符,实现中国日期格式的输入输出。( 类定义、重载运算符的友元函数定义 )

#include<iostream.h>class Date{ public:Date(int y,int m,int d){Year=y;Month=m;Day=d;} friend ostream&operator<<(ostream&stream,Date&date); friend istream&operator>>(istream&stream,Date&date); private:int Year,Month,Day; };ostream&operator<<(ostream&stream,Date&date){ stream<<date.Year<<'/'<<date.Month<<'/'<<date.Day<<endl; return stream; }istream&operator>>(istream&stream,Date&date){ stream>>date.Year>>date.Month>>date.Day; return stream; }

Page 42: 第 11 章   C++ 语言的 I/O 流类库

42

【例 11.18 】重载提取符和插入符,实现中国日期格式的输入输出。 (主函数定义 )

void main( ){ Date d(2003,3,18); cout<<"当前日期是 :"<<d<<"输入新日期 :"; cin>>d; cout<<" 新日期: "<<d;}

上述主程序中,有 5 个插入符 (<<), 有 1 个提取符(>>) ,这些符号中哪些使用了友元函数定义的运算符重载,如何区别是否重载?

Page 43: 第 11 章   C++ 语言的 I/O 流类库

43

11.5 磁盘文件的输入和输出操作 本节中文件指的是磁盘文件。 C++根据文件( file)内容的数据格式,可分为两类:二进制文件和文本文件。文本文件由字符序列组成,也称ASCII码文件,在文本文件中存取的最小信息单位为字符( character),而二进制文件中存取的最小信息单位为字节( Byte)。

11.5.1 文件的打开与关闭

11.5.2 文本文件的读写

11.5.3 二进制文件的读写

11.5.4 随机文件的读写

11.5.5 文件操作的

其它函数

Page 44: 第 11 章   C++ 语言的 I/O 流类库

44

11.5 磁盘文件的输入与输出操作11.5.1 文件的打开和关闭操作1.打开文件①说明一个文件流对象,这又被称为内部文件:ifstream 输入文件流对象名; //只可用于输入ofstream 输出文件流对象名; //只可用于输出fstream 输入输出文件流对象名; //既可用于输入又可用于输出 ②使用对象的成员函数 open打开文件:文件流对象名 .open(" 文件名 " ,打开方式 );此处的文件名必须是外存上的文件名 (外部文件 ) ,必要时应指明路径, open 函数的作用是在内部文件与外部文件之间建立联系。①、②两步可以和二而一 , 即用对象初始化时的构造函数代替 open 函数,如:ifstream 输入文件流对象名 (" 文件名 ",打开方式 )

Page 45: 第 11 章   C++ 语言的 I/O 流类库

45

文件访问方式常量表 (ios 中定义的公有枚举常量 )

方式名称 (16 进制值 ) 含义(用途)in=0x001 以输入方式打开文件 ( 读文件 )

out=0x002 以输出方式打开文件 ( 写文件 )

app=0x008 以输出追加方式打开 ( 在文件末尾追加 )

ate=0x004 打开文件时,文件置文件尾trunc=0x010 清除文件内容或创建新文件binary0x080 以二进制方式打开文件,缺省时为文本nocreate=0x020 打开已有文件,不创建新文件noreplace=0x040 不替换文件内容,若文件存在且未设 app 和 ate ,

则打开失败ios::in|ios::out 以可读可写方式打开文件ios::out|ios::binary 以写二进制文件方式打开 (向文件输出 )

ios::in|ios::binary 以读二进制文件方式打开 ( 从文件输入 )

Page 46: 第 11 章   C++ 语言的 I/O 流类库

46

11.5.1 文件的打开和关闭操作

文件流中三个打开文件的成员函数 (open) 的原型为:void ifstream::open(const char*,int=ios::in, int=filebuf::openprot);void ofstream::open(const char*,int=ios::out, int=filebuf::opernprot);void fstream::open(const char*,int, int=filebuf::openprot); 第一个参数为要打开的磁盘文件名。第二个参数为打开方式,有输入( in),输出( out)等。第三个参数为指定打开文件的保护方式,一般取缺省。例如:要新建磁盘文本文件 file.txt, 并置写状态 , 可如下进行 : ofstream outfile; outfile.open("file.txt",ios::out);或者: ofstream outfile("file.txt",ios::out);或者: ofstream outfile; outfile.open("file.txt");或者: ofstream outfile("file.txt");

Page 47: 第 11 章   C++ 语言的 I/O 流类库

47

11.5.1 文件的打开和关闭操作

 打开文件也应该判断是否成功,若成功,文件流对象值为非零值,不成功为 0( NULL),文件流对象值物理上就是指它的地址。下面是打开一个文件的完整程序段:fstream iofile("myfile.txt",ios::in|ios::out);if(!iofile){cout<<" 不能打开文件 :"<<"myfile.txt"<<endl; exit(1); } //失败退回操作系统2. 关闭文件操作关闭文件将清理与打开的文件有关的内存与外存,这样即可减少内存负担又可保证文件信息安全。关闭文件使用成员函数 close进行:文件流对象名 .close( );// 文件流对象名就是内部文件名。

Page 48: 第 11 章   C++ 语言的 I/O 流类库

48

11.5.2 文本文件的读写操作【例 11.19 】使用插入符向文本文件中写信息

#include<iostream.h>// 定义标准输出流对象 cout#include<fstream.h>// 定义文件流类 fstream#include<stdlib.h>// 定义 abort 函数void main( ){fstream outfile; outfile.open("file1.dat",ios::out); if(!outfile){cout<<"file1.dat打不开! \n";abort();} outfile<<"这是一个程序。 \n"<<"这是一个字符串。 \nok"; outfile.close( );} ①注意分析程序中的 3 个插入符 (<<) 的作用②注意 3 个包含文件的作用③注意该程序的运行结果如何检验④函数 abort( ) 的作用。

Page 49: 第 11 章   C++ 语言的 I/O 流类库

49

【例 11.20 】使用成员函数 getline 将文本文件 file1.dat 中的信息读出并显示在屏幕上。

#include<iostream.h>// 定义标准输出流对象 cout#include<fstream.h>// 定义文件流类 ifstream#include<stdlib.h>// 定义 abort 函数void main( ){ifstream infile("file1.dat"); if(!infile){cout<<"file1.dat打不开! \n";abort();} char s[80]; while(!infile.eof( )) { infile.getline(s,sizeof(s));cout<<s<<endl; } infile.close( );}注意,①定义内部文件与打开文件的格式与例 19 的区别 ②循环条件的含义 ③读取文件信息是否可用提取符?

Page 50: 第 11 章   C++ 语言的 I/O 流类库

50

【例 11.21 】使用成员函数 get 、 put 读写文本文件。

#include<iostream.h>// 定义标准输出流对象 cout#include<fstream.h>// 定义文件流类 fstream#include<stdlib.h>// 定义 abort 函数#include<string.h>// 定义 strlen 函数void main( ){fstream infile,outfile("file2.dat",ios::out); if(!outfile){cout<<"file2.dat打不开! \n";abort( );} char s[]=" 我喜欢 C++程序设计。 "; for(int i=0;i<=(int)strlen(s);i++)outfile.put(s[i]); outfile.close( ); infile.open("file2.dat",ios::in); if(!infile){cout<<"file2.dat打不开! \n";abort( );} char ch;while(infile.get(ch))cout.put(ch); cout<<endl;infile.close( );}注意,该程序中共有几个内部文件?有几个外部文件?内外如何对应?

用操作系统的办法如何显示本程序产生的外部文件 ?

Page 51: 第 11 章   C++ 语言的 I/O 流类库

51

【例 11.22 】编程拷贝文本文件,使用成员函数 get 、 put 读写。

#include<iostream.h>// 定义标准输出流对象 cout

#include<fstream.h>// 定义文件流类 fstream

#include<stdlib.h>// 定义 abort 函数void main( )

{fstream infile("file2.dat",ios::in),outfile("file3.dat",ios::out);

if(!infile){cout<<"file2.dat打不开! \n";abort( );}

if(!outfile){cout<<"file3.dat打不开! \n";abort( );}

char ch;

while(infile.get(ch)) outfile.put(ch);

infile.close( );outfile.close( ); }注意,该程序中共有几个内部文件?几个外部文件?内外如何对应?该程序运行前必须有哪些先决条件?该程序运行后会产生什么结果?用能否用操作系统的办法检验本程序的成果 ?

Page 52: 第 11 章   C++ 语言的 I/O 流类库

52

11.5.3 二进制文件的读写操作 二进制文件打开方式中应指定: ios::binary 写二进制文件使用成员函数: write( ) 读二进制文件使用成员函数: read( ) 这两个函数定义在 fstream.h 中原型分别为 :ostream& write( const char* pch, int n );istream& read( char* pch, int n ); 第 1 个参数要读写的字符指针,即字符串,向文件中写时,

字符串不能被改变; 第 2 个参数为要读写字符的个数。 函数的使用格式为:二进制内部文件名 .write(待写入的字符串地址 , 字符串长度 )二进制内部文件名 .read( 字符串读入地址 , 字符串长度 )

Page 53: 第 11 章   C++ 语言的 I/O 流类库

53

【例 11.23 】使用成员函数 read 、 write 读写二进制文件

#include<iostream.h>// 定义标准输出流对象 cout#include<fstream.h>// 定义文件流类 fstream#include<stdlib.h>// 定义 abort 函数struct person{char name[80];double height;unsigned short age;};person people[5]={"马 ",1.78,35,"张 ",1.68,24,"胡 ",1.90,40,"鲁 ",1.89,50,"郎 ",2,1

8};void main( ){ fstream file("file4.dat",ios::in|ios::out|ios::binary); if(!file){cout<<"file4.dat打不开! \n";abort( );} for(int i=0;i<5;i++) file.write((char*)&people[i],sizeof(people[i])); file.seekp(0,ios::beg);// 文件写指针定位函数,本句定位开头 for(i=0;i<5;i++){file.read((char*)&people[i],sizeof(people[i])); cout<<people[i].name<<'\t'<<people[i].height<<'\t'; cout<<people[i].age<<endl;} file.close( ); }

Page 54: 第 11 章   C++ 语言的 I/O 流类库

54

【例 11.24 】存储本科生、硕士生信息文件 ( 定义本科生 )

#include<iostream.h>// 定义标准输出流对象 cout#include<fstream.h>// 定义文件流类 fstream#include<string.h>// 定义字符串处理函数#include<iomanip.h>// 定义输入输出控制符(控制子)class Student{public:Student(char*pN,unsigned num,double g) {strcpy(Name,pN);uID=num;grade=g;} virtual void Print(ostream &out); friend ostream&operator<<(ostream&out,Student&st); private:char Name[80];unsigned uID;double grade; };void Student::Print(ostream&out){ out.setf(ios::left,ios::adjustfield); out.width(15);out<<Name<<uID; out.setf(ios::right,ios::adjustfield); out.width(8);out<<grade; }ostream&operator<<(ostream&out,Student&st){ st.Print(out); out<<endl; return out; }

Page 55: 第 11 章   C++ 语言的 I/O 流类库

55

【例 11.24 】存储本科生、硕士生信息文件 ( 定义研究生 )

class Master:public Student{public:Master(char*pN,unsigned num,double g,char*pdN): Student(pN,num,g){ strcpy(dName,pdN); } void Print(ostream&out); private:char dName[80]; };void Master::Print(ostream&out){ Student::Print(out); out<<" "<<dName; }void main( ){ ofstream out("abc.txt"); // if(!out){cout<<"abc.txt打不开! \n";abort( );} Student s1("王萍 ",99001,96.5); Master s2("马光 ",99056,84.8,"胡 "), s3("江芳 ",99078,90,"黄 "); out<<s1<<s2<<s3; out.close( ); }注意 ,①插入符是重载的 ,②插入符的重载定义中 ,Print 动态联编。

Page 56: 第 11 章   C++ 语言的 I/O 流类库

56

11.5.4 随机文件的读写操作

从文件头开始读或从文件尾开始写,称为顺序读写 在文件的任意位置进行读写操作,称为随机读写操作,可随机读写的文件称为随机文件。 随机读写文件需要:①按读写方式打开文件②使用定位函数将读写指针定位到所需位置 , 读写指针只有一个,但是有读、写两组函数对它进行操作③使用读写函数对文件进行读写读指针定位的成员函数为: seekg( )

其原型为 istream& seekg( 位置 );

istream& seekg( 偏移量 , 参照位置 );

其中位置和偏移量为长整数,参照位置为下列 3 个之一:ios::beg( 值为 0) 、 ios::cur( 值为 1) 、 ios::end( 值为 2)

分别表示文件开头、当前位置、文件结尾

Page 57: 第 11 章   C++ 语言的 I/O 流类库

57

写指针定位的成员函数为: seekp( )其原型为 ostream& seekp( 位置 ); ostream& seekp( 偏移量 , 参照位置 );其中位置和偏移量为长整数,参照位置为下列 3 个之一:ios::beg( 值为 0) 、 ios::cur( 值为 1) 、 ios::end( 值为 2)分别表示文件开头、当前位置、文件结尾取当前读指针位置的成员函数原型为: long teelg( )取当前写指针位置的成员函数原型为: long teelp( )这两个取指针位置函数返回的长整数表示读写的当前位置距离文件开头的字节数。一般指针定位、取指针位置的函数的使用格式为:内部文件名 .seekp(偏移量 ,参照位置 )内部文件名 .seekg(偏移量 ,参照位置 )内部文件名 . tellg( ) 、内部文件名 . tellp( ) ④文件读写结束应注意使用 close 函数关闭文件

Page 58: 第 11 章   C++ 语言的 I/O 流类库

58

【例 11.25 】存储整数的二进制文件#include<iostream.h>#include<fstream.h>#include<stdlib.h>void main( ){ fstream file("file5.dat",ios::in|ios::out|ios::binary); if(!file){cout<<"file5.dat打不开! \n";abort( );} for(int i=1;i<=20;i++)file.write((char*)&i,4); long pos=file.tellp( );cout<<" 当前字节数 :"<<pos<<endl; for(i=21;i<=50;i++) file.write((char*)&i,4); file.seekp(pos);file.read((char*)&i,4); cout<<" 存储的数据是 :"<<i<<endl; file.seekg(0,ios::beg);for(i=51;i<=100;i++)file.write((char*)&i,4); file.seekg(pos);file.read((char*)&i,4);//此时指针指向哪个整数? cout<<" 存储的数据是 :"<<i<<endl; file.seekp(112,ios::cur);file.read((char*)&i,4); cout<<" 存储的数据是 :"<<i<<endl; //此时的 i 是第几个整数? cout<<" 当前字节数 :"<<file.tellp( )<<endl; file.close( ); }注意 ,①文件中读写数字的方法 ,②读写指针定位的方法。

Page 59: 第 11 章   C++ 语言的 I/O 流类库

59

【例 11.26 】存储对象数组的二进制文件

#include<iostream.h>#include<fstream.h>#include<stdlib.h>void main( ){ struct student {char name[50];long number;double totalscord;}stu[5]= {"马 ",202001,85.5,"李 ",202023,85.9,"郭 ",202045,91.2,"胡 ",20206

7,93.1,"严 ",202076,78.5}; int len=sizeof(student); student s1;fstream file1("file6.dat",ios::out|ios::in|ios::binary); if(!file1){cout<<"file6.dat打不开! \n";abort( );} for(int i=0;i<5;i++)file.write((char*)&stu[i],len); file1.seekg(len*4);file1.read((char*)&s1,len); cout<<s1.name<<'\t'<<s1.number<<'\t'<<s1.totalscord<<endl; file1.seekg(len*1);file1.read((char*)&s1,len); cout<<s1.name<<'\t'<<s1.number<<'\t'<<s1.totalscord<<endl; file1.close( ); }注意 ,①文件中读写对象的方法 ,②读写指针定位的方法。

Page 60: 第 11 章   C++ 语言的 I/O 流类库

60

11.5.5 文件操作的其它函数1.跳过输入流中指定数量的字符的成员函数原型为: istream &ignore(int=1,int=EOF);使用格式为 : 内部文件名 .ignore(跳过字符数 ,终止符 )功能是:从输入流的当前位置跳过指定个数字符,或遇到终止

符为止,终止符仍留在输入流中。隐含终止符为 Ctrl+Z【例 11.27 】正确输入一个整数的程序#include<iostream.h>void main( ){int a;cout<<" 输入一个整数 :";cin>>a; while(!cin){cin.clear( );cin.ignore(80,'\n'); cout<<"再输入一个整数 :" ; cin>>a;} cout<<" 输入的整数是 :"<<a<<endl;} 函数 clear清除错误输入设置的状态字,使输入流恢复正常函数 ignore则跳过一行错误的输入。

Page 61: 第 11 章   C++ 语言的 I/O 流类库

61

2.退回一个字符到输入流中的函数原型为: istream &putback(char);使用格式为 : 内部文件名 .putback(先前输入的字符 )功能是:将指定字符退回输入流中【例 11.28 】将输入的字符串中的数字字符串输出在屏幕上#include<iostream.h>#include<ctype.h>int getnum(char*s){ int flag=0;char ch; while(cin.get(ch)&&!isdigit(ch));// 连续跳过非数字字符 if(!cin) return 0;//遇输入流结束符 (Ctrl+Z)返回 0 do{*s+==ch;}while(cin.get(ch)&&isdigit(ch));// 连续读入数字 *s=0;flag=1;if(cin)cin.putback(ch);return flag; }void main( ){char buf[80]; cout<<" 输入字符串: "; while(getnum(buf)) cout<<" 数字字符串为: "<<buf<<endl; cout<<buf<<endl; } 函数 putback将刚刚读入的非数字字符退回输入流,供再次读入

Page 62: 第 11 章   C++ 语言的 I/O 流类库

62

11.6 字符串流操作  可以把流的概念连接到字符串( string)上。串可以看作字符流。可以用输入输出操作来完成串流的操作。串流是与内存相关,所以也称内存流。类包括 ostrstream 、 istrstream 、strstream 。串流类对象可以保存字符,也可以保存整数、浮点数。串流类对象采用文本方式。其构造函数常用下面几个:istrstream(const char *);// 字符串流输入类istrstream(const char *,int); // 字符串流输入类ostrstream(char *,int,int=ios::out); // 字符串流输出类其中第 2 个参数说明数组大小,第 3 个参数表示字符串流的操作方式,隐含为 out,也可以是 app 、 ate 。 ostrstream 中提供一些成员函数,如①返回流中已插入的字符个数: int pcount( );②返回流存放字符串的数组地址值: char *str( );成员函数的使用方法是 : 流对象名 . 成员函数名 ( )

Page 63: 第 11 章   C++ 语言的 I/O 流类库

63

11.6 字符串流操作

例: char ch, str[36]=”This is a book.\n”;

istrstream input(str);

input>>ch; // 从字符串流对象 ( 串 ) 读入一个字符 cout<<ch<<endl; // 输出’ T’

【例 11.29 】使用字符串流输出 , 注意程序中的 cout 、 out1 的区别#include<iostream.h>

#include<strstrea.h>

void main( )

{ char buf[80];ostrstream out1(buf,sizeof(buf));int m=25;

for(int i=0;i<6;i++)out1<<"m="<<(m+=10)<<';';

out1<<'\0'; cout<<"buf:"<<buf<<endl;double d=123.45678;

out1.setf(ios::fixed|ios::showpoint);

out1.seekp(0);out1<<"d 的值是 :"<<d<<'\0';

cout<<buf<<endl;char *pstr=out1.str( ); cout<<pstr<<endl; }

Page 64: 第 11 章   C++ 语言的 I/O 流类库

64

【例 11.30 】使用字符串流输入 , 注意比较下述 ss 与cin

#include<iostream.h>#include<strstrea.h>void main( ){ int a,a1;double b,b1;char c[80],c1[80]; char buf[]="1234 2.79\n\" 串 \""; istrstream ss(buf); ss>>a>>b>>c; cout<<a+b<<','<<c<<endl; cout<<" 请输入: 1234 2.79回车换行 \" 串 \""; cin>>a1>>b1>>c1; cout<<a1+b1<<','<<c1<<endl;}

Page 65: 第 11 章   C++ 语言的 I/O 流类库

65

【例 11.31 】带字符串流长度的输入 , 比较 s1 与 s2 的区别

#include<iostream.h>

#include<strstrea.h>

void main( )

{ char buf[]="12345yu"; int i,j;

istrstream s1(buf);// 长度有 8 位 istrstream s2(buf,2);// 长度只有 2 位 // 串流中不自动存放串结束符 ('\0')

s1>>i; s2>>j;

cout<<i<<','<<j<<','<<i+j<<endl;

}

Page 66: 第 11 章   C++ 语言的 I/O 流类库

66

11.7 流错误处理流类库中提供错误检测和处理机制。11.7.1 错误状态字和状态函数ios 类中定义有记录错误的状态字和检测、设置、清除状态字的成员函数。

ios 的状态字常量有: goodbit=0x00 表示无错状态 eofbit=0x01 表示读写指针到达文件尾 failbit=0x02 表示读写失败 badbit=0x04 表示试图非法操作 hardbit=0x08 表示出现致命错误

Page 67: 第 11 章   C++ 语言的 I/O 流类库

67

11.7.1 错误状态字和状态函数 ios 中的状态函数有: int rdstate( ) 返回当前错误状态字 int eof( ) 提取操作到达流尾则返回非 0,否则返

回 0 int fail( ) failbit 位设置,返回非 0,否则返回 0 int bad( ) badbit 位设置,返回非 0,否则返回 0 int good( ) 状态字为 goodbit,返回非 0,否则返回 0 void clear(状态字 =ios::goodbit) 使用新状态字设置错误状态位 (隐含设置无错,即清除错误状态字 ) ,但不能清除 hardbit 位

Page 68: 第 11 章   C++ 语言的 I/O 流类库

68

【例 11.32 】测试流对象 cin 错误的状态的程序#include<iostream.h>void main( ){ int a;cout<<" 输入一个整数: ";cin>>a;//正常输入后的状态

字 cout<<"cin.rdstate( ):"<<cin.rdstate( )<<"\ncin.eof( ):"<<cin.eof( ); cout<<"\ncin.bad( ):"<<cin.bad( )<<"\ncin.fail( ):"<<cin.fail( ); cout<<"\ncin.good( ):"<<cin.good( )<<"\n 输入一个字符串 :"; cin>>a; // 错误输入后的状态字 cout<<"\ncin.rdstate( ):"<<cin.rdstate( )<<"\ncin.eof( ):"<<cin.eof

( ); cout<<"\ncin.bad( ):"<<cin.bad( )<<"\ncin.fail( ):"<<cin.fail( ); cout<<"\ncin.good( ):"<<cin.good( )<<endl; cin.clear( ); cout<<"\ncin.good( ):"<<cin.good( )<<endl; cin.clear(ios::failbit);cout<<"cin.good( ):"<<cin.good( )<<endl;}

Page 69: 第 11 章   C++ 语言的 I/O 流类库

69

小结与作业C++中所有的输入与输出均用流来表示输入与输出应注意流的发源地与流向目的地所有对于流的操作均由流对象来实现屏幕作为输出设备是流向的目的地,用 cout表示键盘作为输入设备是流的发源地,用 cin表示插入符 << 是向流对象中输出提取符 >> 是从流对象中获取,保存在内存中文件分内部文件、外部文件,分文本文件、二进制文件,分顺序读写文件、随机读写文件内部文件就是流对象,由 C++ 处理外部文件由操作系统处理,保存在外存中

作业:作业题 11 全做