C++面向对象程序设计计算机与信息学院罗宪第七章 C++的流库(流类库)
本章主要内容:
流库的层次结构(简单介绍)
输出流
输入流
输入 /输出格式控制
文件 I/O流第一节 流库的类层次结构一、流的概念
流( stream):流操作,简称流
在计算机内存中,数据从内存的一个地址移动到另一个地址称为数据流动 ——流操作。
流操作是通过缓冲区( buffer)机制实现的。
缓冲区( buffer):内存的一块区域 ——用作文件与内存交换数据。
将数据从文件中读出:
文件 缓冲区 内存填满内存 缓冲区 文件填满
将数据写入文件:
第一节 流库的类层次结构
在 C++中,把输入设备(如键盘 KB)、输出设备(如显示器 CRT)看成一种文件 ——即输入输出设备均引入缓冲区机制 ——称设备文件。
流操作,KB KB Buffer
CRT CRT Buffer
File File Buffer
C++中输入输出操作通过调用标准流库实现:
KB:标准输入流 ——用标准输入流对象 cin表示
CRT:标准输出流 ——用标准输出流对象 cout表示第一节 流库的类层次结构二、流库的类层次结构
Visual C++6.0包含两种流库,C++老标准流库(其流库文件均带有,h)及新标准流库(其流库文件均不带,h)
㈠ C++老标准流库的类层次结构
C++老标准流库是用继承的方法建立起来的输入 /输
7.3、出( I/O)类库(教材① P343图 7.3、图 7.4)。包括:
ios类:输入 /输出的基类:
⑴ 直接派生:
istream —— 输入流类
ostream —— 输出流类
fstreambase—— 文件流类
strstreambase —— 串流类第一节 流库的类层次结构
⑵ iostream —— 输 入 输 出流 类 ——由 istream 及
ostream派生得到:
class ios;
class istream,virtual public ios;
class ostream,virtual public ios;
class iostream,public istream,public ostream;
⑶ 在 istream,ostream,iostream的基础上重载,=”,
派生出:
class istrema_withassign,public istream;( 输入流辅助类 )
class ostrema_withassign,public ostream;( 输出流辅助类 )
class iostrema_withassign,public iostream;( 输入 /输出流辅助类 )
第一节 流库的类层次结构
⑷ cin,cout,cree,clog对象 ——在 iostream.h中定义:
extrean istrema_withassign cin ;
extrean ostrema_withassign cout ;
extrean ostrema_withassign cerr;
extrean ostrema_withassign clog ;
其中:
cin与标准输入设备 ( 即键盘 ) 相关联
cout 与标准输出设备 ( 即显示器 ) 相关联
cerr 与标准错误输出设备 ( 默认为显示器 ) 相关联
( 非缓冲方式 )
clog与标准错误输出设备 ( 默认为显示器 ) 相关联
( 缓冲方式 )
第一节 流库的类层次结构
㈡ C++新标准流库
C++新标准流库所包含的头文件都不带,h,仍然用继承的方法建立输入输入 /输出( I/O)类库 ——采用模板建立 ——安全性、可扩充性更好。
新标准流库层次结构(教材① P346图 7.8)
新标准流库中常用的头文件(教材① P52):
iostream,当用预定义流对象 cin/cout进行输入 /输出操作时,必须用头文件引入到程序中;
fstream:当使用文件流对象进行文件读写操作时,
必须用头文件引入到程序中;
strstream,当使用字符串流对象对字符串进入输入 /输出操作时,必须用头文件引入到程序中;
sstream:
iomanip:当使用 setw( )等函数进行格式化输入输出时,必须用头文件引入到程序中;
第一节 流库的类层次结构
㈢ 4个预定义标准流对象
在 C++新标准流库中将类模板 basic_istream、
basic_ostream实例化为模板类,并重新定义为 istream、
ostream(以便与旧标准相同)如:
type basic_ostream<char,char_traits<char>> ostream
预定义四个标准流对象:
extern istream cin ;
extern ostream cout ;
extern ostream cerr ;
extern ostream clog ;
——含义与旧标准的相同。
第二节 输出流
在 C++中,将,<<”(即左移运算符)重载为输出运算符;
输出运算符,<<”有二个运算分量,左边(左分量)
为输出流 ostream对象( cout),右边(右分量)为一个基本类型数据
可以重载,<<”输出结构变量或类对象。
一、基本数据类型数据输出二,ostream类中的主要成员函数
1,put( )函数
函数原型声明
ostream &ostream::put( char c);
作用:可用作为二进制数据(字节)或单个字符输出。
调用格式,cout.put(字符 )
第二节 输出流
2,write( )函数
函数原型声明
ostream &ostream::(char *ptr,int n)
作用:向流中输出字符指针变量所指字符串中的 n个字符。
调用格式:
cout.write(字符串,字符个数 )
第二节 输出流
例 【 7.1】 分析程序输出结果,熟悉 put(),write()函数的用法。
#include<iostream>
#include<cstring>
using namespace std;
void main(void)
{ int ch=97;
char str[]="student";
cout.put(ch)<<endl;
cout<<char(ch)<<endl;
cout.write(str,strlen(str))<<endl;
}
第二节 输出流三、重载,<<”运算符
可重新定义,<<”运算符,使之能输出类对象。
例 【 7.2】 重载,<<”运算符,输出 point对象。
第二节 输出流
#include<iostream>using namespace std;
class point{ private:
float x,y,z;
public,point(float a=0,float b=0,float c=0)
{ x=a;y=b;z=c;}friend ostream &operator<<(ostream &os,point p)
//重载输出运算符,<<”
{ os<<"("<<p.x<<","<<p.y<<","<<p.z<<")\n";return os;
//返回输出流类 ostream的一个对象引用 os:即 cout}
};
void main(void){ point p1(1,2,3),p2(4,5,6),p3(7,8,9);
cout<<p1<<p2<<p3<<endl;}
第二节 输出流
1、重载,<<”运算符格式:
ostream &operator<<(ostream &os,point p)
{ 相对于该类对象的输出操作
return os;}
2、说明:
运算符,<<”必须重载为友元;
重载运算符,<<”中,第一个形参必须是输出流
ostream的一个对象引用;第二个形参为输出对象或对象引用
重载运算符返回值应是一个输出流对象引用 ——能连续输出多个对象。
第三节 输入流
在 C++中,将,>>”(即右移运算符)重载为输入运算符;
输入运算符,>>”有二个运算分量,左边(左分量)
为输入流 istream对象( cin),右边(右分量)为一个基本类型数据
可以重载,>>”输入结构变量或类对象。
一,istream类中的主要成员函数
1,get( )函数
函数原型声明
istream &istream::get( char *s,int n,char end);
作用:从输入流中读取一个字符串放到 S所指的缓冲区内,直到遇到 EOF(文件尾)或读入 n-1个字符或遇到 end指定的结束符。
调用格式,cout.get(缓冲区,字符数 +1,结束符 )
第三节 输入流
getline( )函数:与 get()函数功能基本相同;不同点:
get( )读一行字符时,不包括行结束符,而 getline( )函数包括行结束符。
get( )重载为一个字符参数调用格式:
cin.get(字符 );
2,read( )函数
函数原型声明
istream &istream::(char *ptr,int n)
作用:从输入流中读取一个字符串中以数组的形式存放到 ptr所指的缓冲区中。
调用格式:
cin.read(字符串,字符个数 )
例 【 7.3】 分析程序输出结果,熟悉 get(),read()函数的用法。
第三节 输入流
#include<iostream>
#include<cstring>
using namespace std;
void main(void)
{ char ch,ch1;
char str[10]="";
cout<<"输入一个字符,"; cin.get(ch);
cin.get(ch1);//思考为什么?
cout<<"输入一个字符串 (9个字符,或以 #结束 ):";
cin.get(str,10,'#');
cout.put(ch)<<endl;
cout.write(str,strlen(str))<<endl;
cout<<"再输入一个字符串 (1-9个字符 ):";
cin.read(str,9);
cout.write(str,strlen(str))<<endl;
}
第三节 输入流二、重载,>>”运算符
可重新定义,>>”运算符,使之能输出类对象。
例 【 7.4】 重载,>>”运算符,输入 point对象。
第三节 输入流
#include<iostream>using namespace std;
class point{ private:
float x,y,z;public,
point(float a=0,float b=0,float c=0){ x=a;y=b;z=c;}
friend ostream &operator<<(ostream &os,point p){ os<<"("<<p.x<<","<<p.y<<","<<p.z<<")\n";
return os;}
friend istream &operator>>(istream &is,point &p)//重载输出运算符,>>”
{ cout<<"x=";is>>p.x;cout<<"y=";is>>p.y;cout<<"z=";is>>p.z;
return is;//返回输出流类 istream的一个对象引用 is:即 cin
}};
第三节 输入流
void main(void)
{ point p1(4,5,6),p2;
cin>>p2;
cout<<p1<<p2<<endl;
}
1、重载,>>”运算符格式:
istream &operator<<(istream &is,point &p)
{ 相对于该类对象的输出操作
return is;}
第三节 输入流
2、说明:
运算符,>>”必须重载为友元;
重载运算符,>>”中,第一个形参必须是输出流
istream的一个对象引用;第二个形参为输入对象引用
重载运算符返回值应是一个输入流对象引用 ——能连续输入多个对象。
第四节 输入输出格式控制一、用 ios_base类成员函数进行格式控制
㈠ 状态标志字
C++输入输出格式控制可以用状态标志字确定 ——为一个 long int型状态标志。
C++的状态标志位枚举(公有成员,P365表 7.2):
enum {
skipws =ox0001,//跳过输入中的空白,用于输入
left =ox0002,//左对齐输出,用于输出
right =ox0004,//右对齐输出,用于输出
internal =ox0008,//在符号位和基指示符后填入字符,
用于 输出
dec =ox0010,//转换基数为十进制,用于输入输出
oct =ox0020,//转换基数为八进制,用于输入输出
hex =ox0040,//转换基数为十六进制,用于输入输出第四节 输入输出格式控制
showbase =ox0080,//在输出时显示基指示符,用于输入输出
showpoint =ox0100,//输出时显示小数点,用
uppercase =ox0200,//十六进制输出时,表示制式和数值的字符大写
showpos =ox0400,//正整数前显示,+”,用于输出
scientific =ox0800,//用科学记数法表示浮点数,用于输出
fixed =ox1000,//用定点记数法表示浮点数,用于输出
unitbuf =ox2000,//输出操作后即刷新所有流,用于输出
stdio =ox0400,//输出操作后刷新 stdout和 stdree,用于输出
};
第四节 输入输出格式控制
特点:分别使不同的标志位为 1(用二进制表示时)。
如:
skipws ox0001 0000 0000 0000 0001
left ox0002 0000 0000 0000 0010
right ox0004 0000 0000 0000 0100
标志字的表示 ——由各状态标志位组合而成。
如:设置标志字表示在输入时用十进制并跳过空白,
标志字为,0000 0000 0001 0001
即,skipws|dec
㈡ 用于输入输出格式控制的成员函数
1.成员函数表第四节 输入输出格式控制函数原型 功 能
long ios_base::setf(long
flags);
设置状态标志 flags
long ios _base,:unsetf(long
flags);
清除状态标志并返回清除前的标志
long ios _base,:flags( ); 测试状态标志
long ios _base,,flags(long
flags);
设置标志 flags并返回设置前标起
int ios _base,:width(); 返回当前宽度设置值
int ios _base,:width(int w); 设置域宽 w并返回以前的设置
int ios _base,:precision(int
p);
设置小数位数,并返回以前的小数位数
char ios::fill( ) 返回当前的填充字符
char ios::fill( char ch) 设置填充字符 ch,返回当前的填充字符第四节 输入输出格式控制
2.使用方法
⑴ setf(long flags)的使用
格式,流对象,setf(ios::状态标志 )
功能:设置状态标志
说明:流对象通常是 cin,cout等或用户自定义的流对象。
⑵ unsetf(long flags)的使用
格式,流对象,unsetf(ios::状态标志 )
功能:取消状态标志
说明:流对象通常是 cin,cout等或用户自定义的流对象。
例 【 7.5】 分析程序的输出结果(老标准)
第四节 输入输出格式控制
#include<iostream.h>
void main()
{int a=120,b=250;
double x=256.43l,y=130.125;
cout.setf(ios::dec|ios::showpos);
cout<<a<<" "<<b<<endl;
cout.setf(ios::hex|ios::showbase);
cout<<a<<" "<<b<<endl;
cout.setf(ios::fixed);
cout<<x<<" "<<y<<endl;
cout.setf(ios::scientific);
cout<<x<<" "<<y<<endl;
cout.unsetf(ios::scientific|ios::showpos);
cout<<x<<" "<<y<<endl;
}
第四节 输入输出格式控制
格式,流对象,width(int w)
功能:设置域宽并返回以前的设置
例 【 7.6】 分析程序的输出结果
#include<iostream>using namespace std;
void main()
{int a=120,b=250;
double x=256.43l,y=130.125;
cout.width(10);cout<<a<<" "<<b<<endl;
cout<<x<<" "<<y<<endl;
cout.width(10); cout<<a;
cout.width(10); cout<<b<<endl;
cout.setf(ios::left|ios::showpos);cout.width(10); cout<<a;
cout.width(10); cout<<b<<endl;
}
第四节 输入输出格式控制
用 width(int w)设置域宽后,只对紧接着的第一个输出有影响;
用 ios::setf(long flags)设置后对后面所有输出都有影响,直到用 ios::unsetf(long flags)取消为止。
⑷ 设置有效位数或小数位数
格式,流对象,precision(int p)
功能:设置有效位数(小数位数),并返回以前的有效位数(小数位数)
例 【 7.7】 分析程序的输出结果第四节 输入输出格式控制
#include<iostream>using namespace std;
void main()
{double x=256.43l,y=130.125;
cout.precision(5);//设置有效位数为 5
cout<<x<<" "<<y<<endl;cout.precision(4);//设置有效位数为 5
cout<<x<<" "<<y<<endl;
cout.precision(4);
cout.setf(ios::fixed);//表示小数后为四位
cout<<x<<" "<<y<<endl;}
未用 ios::setf()函数设置为定点记数或科学记数法前,
用 ios:,precision(int p)中的 p表示显示的有效位数
ios::setf()函数设置为定点记数或科学记数法后,p表示小数位数。
第四节 输入输出格式控制
⑸ 填充字符
格式,流对象,fill(char ch)
功能,设置填充字符 ch,返回当前的填充字符
例 【 7.8】 分析程序的输出结果
#include<iostream>using namespace std;
void main()
{int a=120,b=250;
cout.setf(ios::left);
cout.width(10);cout.fill('*'); cout<<a;
cout.width(10); cout<<b<<endl;
}
设置填充字符后,对后面的所有输出内容均有效。
第四节 输入输出格式控制二、使用操作符进行输入输出格式控制
使用 ios类中的成员函数进行输入输出格式控制时,
每个函数的调用都需要写一条语句,不能将其嵌入到输入输出语句中。
C++提供了另一种 I/O格式控制的方法 ——操作符。
㈠ C++预定义的操作符
dec 以十进制输入输出整型数,用于输入输出
hex 以十六进制输入输出整型数,用于输入输出
oct 以八进制输入输出整型数,用于输入输出
ws 输入时跳过开头的空白符,用于输入
endl 插入一个换行符并刷新输出流,用于输出
ends 插入一个空字符,用以结束一个字符串,用于输出第四节 输入输出格式控制
flush 刷新一个输入流,用于输入
setbase(int n) 把转换基数设置为 n(n的取值为
0,8,10,16),n的缺省值为 0(以十进进制形式输出)。
resetiosflags(long f) 关闭由参数 f指定的格式标志,用于输入输出
setiosflags(long f) 设置由参数 f指定的格式标志,
用于输入输出
setfill(char c) 设置填充字符
setprecision(int n) 设置数据小数位数,缺省时为 6,用于输入输出
setw(int n) 设置域宽,用于输入输出
其中,setiosflags(long f),resetiosflags(long
f) 中的 long f为格式标志 ——同上。
第四节 输入输出格式控制
例 【 7.9】 分析程序的输出结果
#include <iostream>#include <iomanip>
using namespace std;
void main()
{ cout <<" i i^2 i^3\n";
int i;for (i=1;i<=5;i++)
{ cout <<setw(10)<<i<<setw(10)<<i*i;
cout<<setw(10)<<i*i*i<<endl;
}
}
第五节 文件 I/O流一、文件的概念
C++中的文件是一个字符流或二进制流 ——流式文件;
文件的分类:按文件的组织形式分为字符文件 (ASCII
文件 )、二进制文件;
文件的操作:打开文件、文件的读 /写、文件的关闭二、文件的打开与关闭
㈠ 文件处理的三个流类
iftream—文件输入流类,用于文件的输入(读文件)
oftream—文件输出流类,用于文件的输出(写文件)
ftream—文件输入 /输出流类,用于文件的输入 /输出
(读 /写文件)
当程序中进行文件操作时,应加上头文件,fstream.h”
第五节 文件 I/O流
若要打开文件进行读写操作,必须定义相应的流对象。
如:
iftream in; //文件输入流对象
oftream out; //文件输出流对象
ftream both; //文件输入 /输出流对象
㈡ 文件打开
1、文件打开用成员函数 open( )
原型:
void open( const char *s,ios_base::openmode,
mode=ios_base::out|ios_base::trunc)
其中,第一个参数表示打开的文件,第二个参数表示文件打开方式,第三个参数表示访问方式。
第五节 文件 I/O流
2、文件打开的方法:
方法一:先定义一个文件流对象,再用文件流对象调用成员函数 open( )打开一个文件。如:
ifstream f1; //定义文件输入流对象 f1
f1.open(,d:\\vcprg\\e301.cpp”); //打开 D盘 vcprg文件夹(目录)下的 e301.cpp文件,可进行文件读操作
方法二:在定义文件流对象时打开文件。如:
ifstream f1(,d:\\vcprg\\e301.cpp”)
用文件流对象打开文件后,该对象就代表了被打开的文件。
例 【 7.10】 打开 D盘 vcprg文件夹(目录)下的
e301.cpp文件,并显示文件中的内容。
第五节 文件 I/O流
#include<iostream>
#include<fstream>
using namespace std;
void main(void)
{ ifstream f1; //定义文件输入流对象
f1.open("d:\\vcprg\\e301.cpp"); //打开文件
char c;
while(f1) //判断文件未结束则循环
{ f1.get(c);
cout<<c;
}
cout<<endl;
f1.close();//关闭文件
}
第五节 文件 I/O流
3、文件访问方式
文件的访问方式如教材① P379表 7.3
当用 ifstream定义流对象并打开一个文件时,默认为
ios_base::in方式;
当用 ofstream定义流对象并打开一个文件时,默认为
ios_base::out方式;
当用 fstream定义流对象并打开一个文件时,应给出打开方式(可用“位或”运算以多种方式打开文件)。
如:
fstream f;
f.open(“file.cpp”,ios_base::in|ios_base::out)
例 【 7.11】 将 D盘 vcprg文件夹(目录)下的 e301.cpp
文件,复制为 text.txt文件。
第五节 文件 I/O流
#include<iostream>#include<fstream>
using namespace std;
void main(void)
{ ifstream f1; //定义文件输入流对象
ofstream f2;f1.open("d:\\vcprg\\e301.cpp"); //以读方式打开文件
f2.open("d:\\vcprg\\text.txt"); //以写方式打开文件
char c;
while(f1) //判断文件未结束则循环
{ f1.get(c);f2<<c;
}
cout<<"文件复制成功 "<<endl;
f1.close();//关闭文件
f2.close();}
第五节 文件 I/O流
如果采用二进制方式打开文件,则打开方式应设置为:
ios_base::binary
例 【 7.12】 将 D盘 vcprg文件夹(目录)下的 e301.cpp
文件,复制为二进制文件 text.dat。 (e712.cpp)
例 【 7.13】 打开 D盘 vcprg文件夹(目录)下的二进制文件 text.dat,并显示文件中的内容。 (e712.cpp)
4、文件异常处理
当打开一个文件可能失败,在程序中通常应加上异常处理程序。
⑴ 用条件语句判断文件流标志位 failbit是否为 true。
if(!文件流对象 ){<异常处理程序 >}
if(文件流对象 ){<正常处理程序 >}
第五节 文件 I/O流
⑵ 用成员 fail()函数
if(文件流对象,fail()){<异常处理程序 >}
if(!文件流对象,fail()){<正常处理程序 >}
5、文件的关闭
格式,文件流对象,close();
三、文件的读写
1、文本文件的读写
读文件格式,文件输入流对象,get(char);
写文件格式,文件输出流对象,put(char ch);
第五节 文件 I/O流
2、二进制文件的读写
二进制文件的读 /写分别用成员函数 read( ),wrige( )
实现。
写二制文件的格式:
输出文件流对象,write((char*)&对象或 &对象数组名 [下标 ]
,sizeof(对象名或所属类名 ));
读二进制文件的格式输入文件流对象,read((char*)&对象或 &对象数组名 [下标 ]
,sizeof(对象名或所属类名 ));
第五节 文件 I/O流
例 【 7.14】 输入多个人的信息( person类),将其存入到 D盘 vcprg文件夹的 personal.dat文件中,然后再依次输出。( person类的定义在 person.h中,其中
date类的定义在 date.h中)。
#include<iostream.h>#include<fstream.h>
#include<stdlib.h>#include"person.h"
void main(void)
{ char na[11],se[3];int y,m,d;
person psn;ifstream f1;ofstream f2;
f2.open("d:\\vcprg\\personal.dat",ios::binary);
if(!f2){ cout<<"\a can't open personal.dat";
exit(1);}
第五节 文件 I/O流
char ch='y';while(ch=='y')
{ cout<<"请输入学生信息,\n";cout<<"姓名,";cin>>na;
cout<<"性别,";cin>>se;
cout<<"出生年,";cin>>y;cout<<"出生月,";cin>>m;
cout<<"出生日,";cin>>d;psn.set_person(na,se,y,m,d);
f2.write((char*)&psn,sizeof(person));
cout<<"继续吗 (y/n)? ";cin>>ch;}
f2.close();f1.open("d:\\vcprg\\personal.dat",ios::binary);
if(!f1) //或者用 if(f1.fail())
{ cout<<"\a can't open personal.dat";exit(1);
}
第五节 文件 I/O流
while(!f1.eof()){ f1.read((char*)&psn,sizeof(person));
cout<<psn.get_name()<<"\t";cout<<psn.get_sex()<<"\t";
cout<<psn.get_year()<<"\t";
cout<<psn.get_month()<<"\t";cout<<psn.get_day()<<"\t";
cout<<endl;}
f1.close();
}