C++面向对象程序设计学习笔记(8)
C++流类库与输入输出
C++流概述
C++的输入输出流
C++中,流指的是数据从一个源流到一个目的的抽象,它负责在数据的生产者和数据的消费者之间建立联系,并管理数据的流动
从流中提取数据称为输入操作,向流中添加数据称为输出操作
C++的输入输出是以字节流的形式实现的,文件和字符串也可视为有序的字节流,称为文件流及字符串流
在c++中的I/O流类库中包含了许多用于输入输出的类,称为流类,用流类定义的对象称为流对象
用于输入输出的流对象
C++编译系统提供了用于输入输出的iostream类库,
常用的头文件有
iostream //包含了对输入输出流进行操作所需的基本信息,使用cin、cout等流对象进行针对标准设备的I/O操作时,需包含此头文件
fstream //用于用户管理文件的I/O操作。使用文件流对象进行针对磁盘文件的操作时,需包含此头文件
strstream//用于字符串流的I/O操作。使用字符串流对象进行针对内存字符串空间的I/O操作时,需包含此头文件
iomanip //用于输入输出的格式控制。在使用setw、fixed等大多数操作符进行格式控制时,需包含此头文件
用于输入输出的流类
I/O类库中常用流类
类名 | 说明 | 头文件 |
---|---|---|
ios | 流基类 | iostream |
istream | 通用输入流类和其他输入流的基类 | iostream |
ostream | 通用输出流类和其他输出流的基类 | iostream |
iostream | 通用输入输出流类和其他输入输出流的基类 | iostream |
ifstream | 输入文件流类 | fstream |
ofstream | 输出文件流类 | fstream |
fstream | 输入输出文件流类 | fstream |
istrstream | 输入字符串流类 | strstream |
ostrstream | 输出字符串流类 | strstream |
strstream | 输入输出字符串流类 | strstream |
预定义的流对象
用流类定义的对象称为流对象,与输入设备相联系的流对象称为输入流对象,与输出设备相联系的流对象称为输出流对象
C++包含标准输入流对象cin、标准输出流对象cout、非缓冲型的标准出错流对象cerr和缓冲型的标准出错流对象clog等预定义的流对象
cerr和clog的区别是,cerr不经过缓冲区,直接向显示设备输出有关关系,clog的信息存放在缓冲区中,当缓冲区满后或遇上endl时向显示设备输出
输入输出流的成员函数
C++程序中除了用cout和插入运算符"<<"实现输出,用cin和提取运算符">>"实现输入为,还存在一些成员函数
put函数
put函数用于输出一个字符,格式如下cout.put(char c)
例
cout.put('A');
cout<<'A';
输出结果为
AA
get函数
get函数功能与提取运算符">>"类似,不同在于get函数在读入数据时可包括空白字符,而提取运算符在默认情况下拒绝接受空白字符
格式为cin.get(ch)
作用为从输入流中读取一个字符(包括空白符),赋给字符变量ch,成功返回非0值,失败返回0值
例
char a,b;
cin>>a;
cin.get(b);
cout<<a<<' '<<b<<endl;
当输入aa时,文件输出
a a
getline函数
getline调用形式为
cin.getline(字符数组,字符个数n,终止标示符);
或
cin.getline(字符指针,字符个数n,终止标示符);
用于从输入流中读入n-1个字符,赋给相应的字符数组或字符指针,若读入过程中遇见指定的终止字符,则提前终止输入,最后插入字符串终止标志'\n'
注意
1.cin.getline只能用于输入字符型数据
2.cin.getline可以读入包括空格符在内的一系列字符
例
char s[100005];
cin.getline(s,10); //输入:aahcihbiuah
cout<<s<<endl; //输出:aahcihbiu
cin.getline(s,5,'\0'); //输入:123456
cout<<s<<endl; //输出:1234
cin.getline(s,5,'2'); //输入:123456
cout<<s<<endl; //输出:1
cin.getline(s,13,'a'); //输入:123456789 1234
cout<<s<<endl; //输出:123456789 12
ignore函数
调用形式为cin.ignore(n,终止字符)
ignore函数的功能时跳过输入流中的n个字符(默认为1),或在遇到指定终止字符(默认为EOF)时提前结束
例
char s[100005];
cin.ignore(10); //输入:aahcihbiuah
cin>>s;
cout<<s<<endl; //输出:h
cin.ignore(10,'h'); //输入:aahcihbiuah
cin>>s;
cout<<s<<endl; //输出:cihbiuah
预定义类型的输入输出
预定义类型的输入输出指C++预定义标准类型的输入输出
插入运算符与提取运算符
一般使用格式为
cin>>变量; //输入
cout<<变量; //输出
插入运算符"<<"
插入运算符"<<"本身为左移运算符,在iostream中进行了重载,其原型为
ostream &operator<<(ostream &类型名)
提取运算符">>"
提取运算符">>"本身为右移运算符,在iostream中进行了重载,其原型为
istream &operator>>(istream &类型名)
注意:
默认情况下,运算符">>"将跳过空白符
输入输出的格式控制
C++提供两类输入输出的格式控制的方法,
一种为使用ios类中有关格式控制的流成员函数进行格式控制,另一种是使用称为操纵符的特殊类型函数进行格式控制
使用流成员函数进行格式控制
用于控制输入输出的流成员函数
流成员函数 | 功能 |
---|---|
setf(flags) | 设置状态标志 |
unsetf(flags) | 清楚状态标志 |
width(n) | 设置字段域宽为n位 |
fill(char ch) | 设置填充字符ch |
precision(n) | 设置实数精度为n位,在普通十进制小数形式输出时n代表有效数字,在fixed(固定小数位数)或scientific(指数)形式输出时n代表小数位数 |
状态标志在类ios中被定义为枚举值,所以在引用时前要加"ios::"
状态标志 | 功能 | 输入/输出 |
---|---|---|
ios::skipws | 跳过输入中的空白符 | 输入 |
ios::left | 输出数据在本域宽范围内左对齐 | 输入 |
ios::right | 输出数据在本域宽范围内右对齐 | 输入 |
ios::internal | 数据符号位左对齐,数据本身右对齐 ,符号和数据之间为填充符 | 输入 |
ios::dec | 设置整数基数为10 | 输入/输出 |
ios::oct | 设置整数基数为8 | 输入/输出 |
ios::hex | 设置整数基数为16 | 输入/输出 |
ios::showbase | 输出整数时显示基数符号(八进制以0打头,十六进制以0x打头) | 输入/输出 |
ios::showpoint | 浮点数输出时带有小数点 | 输出 |
ios::uppercase | 以科学表示法格式E和以十六进制输出字符时用大写表示 | 输出 |
ios::showpos | 正整数前显示'+'符号 | 输出 |
ios::scientific | 用科学表示法显示浮点数 | 输出 |
ios::fixed | 用定点格式显示浮点数 | 输出 |
ios::unitbuf | 完成输出操作后立即刷新所有的流 | 输出 |
ios::stdio | 完成输出操作后立即刷新stdout和stderr | 输出 |
1.设置状态标示的一般格式为
流对象.setf(ios::状态标志);
注意:
1.由于状态标志在类ios中被定义为枚举值,所以在引用时前要加"ios::"
2.在设置多项标示时,中间应使用或运算符'|'分隔
2.清除状态标示的一般格式为
流对象.unsetf(ios::状态标志);
3.设置域宽的一般格式为
流对象.width(int n);
注意:每次设置的域宽只对下一次操作有效,每次操作结束后,域宽回复为默认域宽0
4.设置实数精度的一般格式为
流对象.width(int n);
设置实数精度为n位,在普通十进制小数形式输出时n代表有效数字,在fixed(固定小数位数)或scientific(指数)形式输出时n代表小数位数
例:
cout.setf(ios::right | ios::fixed);
cout.width(10);
cout<<"1234"<<endl; //域宽为10
cout<<"1234"<<endl; //域宽为0
double PI=acos(-1.0);
cout.precision(10);
cout<<PI<<endl; //固定小数位数形式
cout.unsetf(ios::fixed);
cout<<PI<<endl; //普通十进制小数形式
cout.setf(ios::scientific);
cout<<PI<<endl; //指数形式
输出结果
1234
1234
3.1415926536
3.141592654
3.1415926536e+000
5.填充字符的一般格式为
流对象.fill(char c);
作用为当域宽不满时输出字符c来填充,默认状态下为空格
例:
cout.setf(ios::right | ios::fixed);
cout.width(10);
cout.fill('c');
cout<<1<<endl;
输出结果
ccccccccc1
使用操纵符进行格式控制
C++除了可以使用ios类中有关格式控制的流成员函数进行格式控制,还可以使用称为操纵符的特殊类型函数进行格式控制。
所有不带形参的操纵符都定义在头文件iostream.h上,带形参的则定义在头文件iomanip.h上
C++预定义的操纵符
操纵符 | 功能 | 输入/输出 |
---|---|---|
ws | 跳过输入开头的空白符 | 输入 |
endl | 输出换行符并刷新输出流 | 输出 |
ends | 插入一个空字符null | 输出 |
flush | 刷新输出流 | 输出 |
dec | 设置整数基数为10 | 输入/输出 |
oct | 设置整数基数为8 | 输入/输出 |
hex | 设置整数基数为16 | 输入/输出 |
setbase(n) | 设置整数基数为n(n=0,8,10,16),默认为0,即以十进制输出 | 输入/输出 |
setfill(c) | 设置填充字符c,默认为空格 | 输出 |
setprecision(n) | 设置实数精度为n位,在普通十进制小数形式输出时n代表有效数字,在fixed(固定小数位数)或scientific(指数)形式输出时n代表小数位数 | 输出 |
setw(n) | 设置字段域宽为n位 | 输出 |
setiosflags(f) | 设置状态标志f | 输入/输出 |
resetiosflags(f) | 清除状态标志f | 输入/输出 |
操纵符 | 功能 |
---|---|
setiosflags(ios::left) | 输出数据在本域宽范围内左对齐 |
setiosflags(ios::right) | 输出数据在本域宽范围内右对齐 |
setiosflags(ios::fixed) | 用定点格式显示浮点数 |
setiosflags(ios::scientific) | 用科学表示法显示浮点数 |
setiosflags(ios::showpos) | 正整数前显示'+'符号 |
setiosflags(ios::uppercase) | 以科学表示法格式E和以十六进制输出字符时用大写表示 |
例
cout<<setw(10)<<1234<<2345<<endl;
cout<<setprecision(10)<<setiosflags(ios::fixed)<<acos(-1.0)<<endl;
cout<<setprecision(10)<<acos(-1.0)<<endl;
cout<<setprecision(10)<<resetiosflags(ios::fixed)<<acos(-1.0)<<endl;
cout<<setw(10)<<setfill('c')<<1234<<endl;
输出结果
12342345
3.1415926536
3.1415926536
3.141592654
cccccc1234
使用用户自定义操纵符进行格式控制
如为输出流定义操纵符函数,形式如下
ostream &操纵符名 (ostream &stream)
{
自定义代码
return stream;
}
如为输如流定义操纵符函数,形式如下
istream &操纵符名 (istream &stream)
{
自定义代码
return stream;
}
例
ostream &outputd(ostream &stream)
{
stream<<setprecision(10)<<setiosflags(ios::fixed);
return stream;
}
int main()
{
cout<<outputd<<acos(-1.0)<<endl;
return 0;
}
输出结果为
3.1415926536
用户自定义类型的输入输出
C++通过重载运算符">>"和"<<"来实现用户自定义类型的输入输出
重载插入运算符
定义插入运算符"<<"重载函数的一般格式如下
ostream &operator<< (ostream &out,user_name& obj)
{
out<<obj.item1;
......
out<<obj.itemn;
return out;
}
user_name为用户自定义的类型名,out是ostream类对象的引用,obj为用户自定义的类型名user_name的引用,item1、.......itemn为用户自定义类型中的数据成员
重载插入运算符函数不能是所操作的类的成员函数,但可以是该类的友元函数或普通函数
重载提取运算符
定义提取运算符">>"重载函数的一般格式如下
istream &operator>> (istream &in,user_name& obj)
{
in>>obj.item1;
......
in>>obj.itemn;
return in;
}
user_name为用户自定义的类型名,in是istream类对象的引用,obj为用户自定义的类型名user_name的引用,item1、.......itemn为用户自定义类型中的数据成员
重载提取运算符函数不能是所操作的类的成员函数,但可以是该类的友元函数或普通函数
文件的输入输出
C++将文件是为字符序列,即文件是由一个一个字符数据顺序组成的,根据数据的组织形式,文件可以分为文本文件和二进制文件。文本文件又称ASCII文件,他的每个字节存放一个ASCII代码,代表一个字符。二进制文件则是把内存中的数据,按其在内存中的存储形式原样写到磁盘中存储。
C++中引入流式文件的概念,即文件一律视为字符构成的序列,即字符流
文件的打开与关闭
C++中要进行文件的操作,首先要创造一个流对象,然后将这个流对象与文件相关联,即打开文件,此后才能进行文件的读写操作,最后关闭文件。
文件的打开
c++打开一个文件,即建立一个流对象与文件相关联,关闭一个文件,就是取消这种关联
用于文件输入输出的文件流类
类名 | 说明 | 功能 |
---|---|---|
ifstream | 输入文件流类 | 用于文件输入 |
ofstream | 输出文件流类 | 用于文件输出 |
fstream | 输入输出文件流类 | 用于文件输出/输入 |
头文件为fstream.h
使用open函数打开文件的一般形式
文件流对象.open(文件名,打开方式);
文件名可包括路径,默认路径为当前目录
打开方式见下表
方式 | 功能 |
---|---|
ios::app | 打开一个输出文件,用于将数据添加至文件尾部 |
ios::ate | 打开一个现存文件,将文件指针移至文件末尾 |
ios::in | 打开一文件,以便进行输入 |
ios::nocreate | 打开一个文件,若文件不存在,则打开失败 |
ios::noreplace | 打开一个文件,若文件存在,则打开失败 |
ios::out | 打开一文件,以便进行输出 |
ios::trunc | 打开一个文件,若文件存在,则删除其中所有数据,如不存在,则建立新文件 |
ios::binary | 以二进制方式打开一个文件,默认问文本文件 |
注意:
1.使用"ios::app"时,文件必须存在,打开时文件指针位于文件末尾,此方式只能用于输出
2.使用"ios::ate"时,打开时文件指针位于文件末尾,数据可写入文件任意位置
3.使用"ios::in"时,文件必须存在,此方式只能用于输入,使用ifstream时默认使用该方式
使用"ios::out"时,此方式只能用于输出,使用ofstream时默认使用该方式
4.新版本I/O类库中不提供ios::nocreate 与 ios::noreplace
5.若指定"ios::out"方式,且未指定"ios::ate"或"ios::app"方式,则隐含"ios::trunc"方式
6.当文件需要使用多种方式打开时,使用"|"操作符组合多种方式
7.可以通过定义文件流对象时指定参数,调用构造函数来实现打开文件功能
8.当文件打开操作失败,流对象的值为0
文件的关闭
使用close函数关闭文件,即将文件与文件流对象脱钩
使用close
函数关闭文件的一般形式
文件流对象.close();
例: 使用文件流输出字符串
string a="1dascipan";
ofstream out;
out.open("1.txt",ios::out);
out<<a<<endl;
out.close();
二进制文件的读写
二进制文件的读写大致与文件读写一致,但要求在打开文件使用ios::binary
以二进制形式传送和存储
二进制文件进行读写有两种方式,一是使用函数get
和put
,另一种是使用函数read
和write
1.使用函数get
和put
例: 使用二进制方式编写一个二进制文件并读取
void bt_wirte()
{
ofstream outb("out.dat",ios::binary);
for(char i='a';i<='z';i++) outb.put(i);
outb.close();
}
void bt_read()
{
ifstream inb("out.dat",ios::binary);
char c;
while(inb.get(c)) cout<<c;
cout<<endl;
inb.close();
}
int main()
{
bt_wirte();
bt_read();
return 0;
}
输出结果
屏幕输出
abcdefghijklmnopqrstuvwxyz
并输出一个名为out.dat的二进制文件
2.使用函数read
和write
c++提供了函数read
和write
用于读写一个数据块
调用格式如下
inf.read(char *buf,int len);
outf.write(const char *buf,int len);
read是流类istream中的成员函数,有两个参数,第一个参数buf为指针,指向读入数据存储空间的起始地址,第二个参数len为读入字节数。read的功能是从inf关联的文件中读取len个或读至EOF的字节,将其存入buf指向的内存空间中
write是流类ostream中的成员函数,有两个参数,第一个参数buf为指针,指向数据存储空间的起始地址,第二个参数len为输出字节数。read的功能是将其存入buf指向的内存空间中字节开始的len个字节的数据写入outf所关联的文件中
注意:
指针默认的数据类型为char * ,若为其他数据类型,则需要进行转换,并通过sizeof函数确定读入字符数
例:
void bt_wirte()
{
char s[100]="why";
ofstream outb("out.dat",ios::binary);
outb.write((char *)&s,sizeof(s));
outb.close();
}
void bt_read()
{
char s[100];
ifstream inb("out.dat",ios::binary);
inb.read((char *)&s,sizeof(s));
cout<<s<<endl;
inb.close();
}
int main()
{
bt_wirte();
bt_read();
return 0;
}
输出结果
屏幕输出
why
并输出一个名为out.dat的二进制文件
3.检测文件结束
采用文件流读写文件时,可通过成员函数eof()
实现检测文件结束
一般形式为
ifstream inf;
......
if(!inf.eof()) ......
当返回值为零时,表示为到达文件尾,非零时,表示到达文件尾
也可通过检测文件流对象是否为零,若为零则表示文件结束
4.二进制文件的随机读取
前面的文件操作是按一定顺序来进行读写的,因此称为顺序文件,C++在类istream和类ostream定义了几个与文件指针相关的成员函数,使其能实现文件的随机读取
istream
tellg(); //返回输入文件读指针的位置
seekg(文件中的位置); //将输入文件读指针移至指定的位置
seekg(位移量,参照位置); //以参照位置为基准移动若干字节
文件中的位置和位移量都是long型整数,以字节为单位,
参照位置可为
ios::beg //文件开头
ios::cur //文件指针当前位置
ios::end //文件末尾
ostream
tellp(); //返回输出文件读指针的位置
seekp(文件中的位置); //将输出文件读指针移至指定的位置
seekp(位移量,参照位置); //以参照位置为基准移动若干字节
seekg
与seekg
的第二个参数可以省略,此时参照位置为ios::beg
若为即可输出也可输入的文件,则seekg
与seekg
都可使用
命名空间和头文件命名规则
命名空间
命名空间实际上是一个由程序设计者命名的内存区域,程序设计者可根据需要指定一些有名字的命名空间,将各命名空间中声明的标识符与该命名空间标识符相关联。
声明命名空间的一般形式为
namespace NS
{
.......
}
namespace是定义命名空间必须的关键字,NS 是命名空间的名字,花括号为命名空间的作用域