流类简介
在C++的标准类库中,将与数据输入/输出相关的类统称为“流类".
istream类提供了流的大部分输入操作,对系统预定义的所有输入流重载提取运算符“>>”。ostream类对系统预定义的所有输出流重载插入运算符“<<”。
表7-1 常用I/O流类列表
类名 | 说明 | 包含文件 | |
---|---|---|---|
抽象流基类 | ios | 流基类 | ios |
输入流类 | istream | 通用输入流基类和其他输入流基类。cin是该类的对象 | istream |
ifstream | 文件输入流类。用于从文件读取数据 | fstream | |
输出流类 | ostream | 通用输出流基类和其他输出流基类。cout是该类的对象 | ostream |
ofstream | 文件输出流类。用于向文件写入数据 | fstream | |
输入输出流类 | iostream | 通用输入/输出流基类和其他输入/输出流基类 | istream |
fstream | 文件输入/输出流类。既能从文件中读取数据,也能向文件中写入数据 | fstream |
常见的头文件有以下三个。
- iostream
包含操作所有输入/输出流所需的基本信息。 - iomanip
包含格式化I/O的带参数操纵符,可用于指定数据输入/输出的格式。 - fstream
包含处理文件的有关信息。
标准流对象
标准流对象是为用户提供的常用外设与内存之间通信的通道,对数据进行解释和传输,提供必要的数据缓冲等。
C++在头文件iostream中为用户预定义了4个标准流对象,分别是cin(标准输入流)、cout(标准输出流)、cerr(非缓冲错误输出流)和clog(缓冲错误输出流)。
重定向,是改变默认的输入来源,或改变默认的输出目的地。
重定向函数freopen的原型
FILE *freopen(const char *path,const char *mode,FILE *sream);
freopen()功能是将stream按mode指定的模式重定向到路径path指向的文件。
程序7-1 将标准输出cout重定向到文件
#include
using namespace std;
int main()
{
int x,y;
cin>>x>>y;
freopen("test.txt","w",stdout); //将标准输出重定向到文件test.txt
if(y==0) //除数为0则输出错误信息
cerr<<"error."<
编写程序,将从键盘读取10个整数更改为读取文件中的前10个整数,计算平均值
程序7-2 标准输入重定向为文件
#include
using namespace std;
int main()
{
int x,count,sum = 0;
freopen("input.dat","r",stdin); //将标准输入重定向到文件input.dat
for(count = 0;count<10;count++)
{
cin>>x;
sum+=x;
}
cout<<"前10个整数的平均值 = "<<1.0*sum/10<
表7-2 ios中错误状态字
标识常量 | 值 | 含义 |
---|---|---|
goodbit | 0X00 | 流状态正常 |
eofbit | 0X01 | 文件结束符 |
failbit | 0X02 | I/O操作失败,数据未丢失,可以恢复 |
badbit | 0X04 | 非法操作,数据丢失,不可恢复 |
在ios类中还有几个与流状态相关的成员函数。
- 返回流是否结束 int eof() const
返回eofbit的值。 - 返回流是否处于正常状态 int fail() const
返回failbit状态 - 判定流是否正常 int good() const;int operator void *();
- 返回流是否处于失败状态 int bad() const; int operator void !();
- 返回状态字 int rdstate() const;
返回流的当前状态 - 恢复状态字 void clear(int nStata = 0);
程序7-3 标准输入重定向为文件并判断文件状态
#include
using namespace std;
int main()
{
int x,count,sum = 0;
freopen("input.dat","r",stdin); //将标准输入重定向到文件input.dat
for(count=0;count<10;count++)
{
cin>>x;
sum+=x;
if(cin.eof()) break;
}
cout<<"前"<
程序7-4 将从键盘输入的数据进行累加
#include
#include
using namespace std;
int main()
{
char ch;
int sum = 0,count=0,x;
cout<<"请输入整数(按Ctrl+Z退出)"<>x) //使用组合键 结束输入
{
sum += x; //计算累加和
count++;
}
cout<<"确实要退出输入请按 Q";
cin.clear(); //状态字清0,恢复流状态
cin>>ch;
}while(toupper(ch)!='Q');
cout<<"sum="<
控制I/0格式
c++进行I/O格式控制的方式一般有使用流操作符
、设置标志字
和调用成员函数
。
流操作符
表7-3 C++中常见数据类型的默认I/O格式
I/O的数据类型 | 默认的输入格式 | 默认的输出格式 |
---|---|---|
short、int、long(signed、unsigned) | 与整型常数相同 | 一般整数形式,负数前面有负号 |
float、double、long double | 与浮点数相同 | 浮点或指数格式,取决于哪个更短 |
char(signed、unsigned) | 第一个非空白字符 | 单个字符(无引号) |
char *(signed、unsigned) | 从第一个空白字符(空格、\t、\n等)开始到下一个空白字符结束 | 字符序列(无引号) |
void * | 无前缀的十六进制数 | 无前缀的十六进制数 |
Bool | 将true或1识别为true,将false或0识别为false | 1或0 |
C++在iostream中提供了一些常用的无参数的流操作符。
表7-4 iostream中常用流操作符
流操作符 | 作用 | 输入/输出 |
---|---|---|
endl | 输出一个新行符,并清空流 | O |
ends | 输出字符串结束,并清空流 | O |
flush | 清空流缓冲区 | O |
dec * | 以十进制形式输入或输出整数 | I/O |
hex | 以十六进制形式输入或输出整数 | I/O |
oct | 以八进制形式输入或输出整数 | I/O |
ws | 提取空白字符 | O |
注:“流操作符”栏中的星号“*”不是操作符的一部分,表示是默认设置。
在头文件iomanip中定义了一些用于格式控制的流操纵符。
表7-5 常用的用于格式控制的流操纵符
流操纵符 | 作用 |
---|---|
fixed | 以普通小数形式输出浮点数 |
scientific | 以科学计数法形式输出浮点数 |
left | 左对齐,即在宽度不足时将填充字符添加到右边 |
right | 右对齐,即在宽度不足时将填充字符添加到左边 |
setbase(int b) | 设置输出整数时的进制,b为8、10或16 |
setw(int w) | 指定输出宽度为w个字符,或输入字符串时读入w个字符。一次有效 |
setfill(int c) | 在指定输出宽度的情况下,输出的宽度不足时用ASCII码为c的字符填充(默认情况是用空格填充) |
setprecision(int n) | 设置输出浮点数的精度为n。在使用非fixed且非scientific方式输出的情况下,n即为有效数字最多的位数。如果有效数字位数超过n,则小数部分四舍五入,或自动变为科学计数法输出并保留一共n位有效数字;在使用fixed方式和scientific方式输出的情况下,n是小数点后面应保留的位数 |
setiosflags(fmtfalgs f) | 通用操纵符。将格式标志f所对应的格式标志位置为1 |
resetiosflags(fmtfalgs f) | 通用操纵符。将格式标志f所对应的格式标志位置为0(清除) |
boolapha | 把true和false输出为字符串 |
noboolalpha * | 把true和false分别输出为1和0 |
showbase | 输出表示数值进制的前缀 |
noshowbase * | 不输出表示数值进制的前缀 |
showpoint | 总是输出小数点 |
noshowpoint * | 只有当小数部分存在时才显示小数点 |
showpos | 在非负数值中显示+ |
noshowpos * | 在非负数值中不显示+ |
skipws * | 输入时跳过空白字符 |
noskipws | 输入时不跳过空白字符 |
uppercase | 十六进制数中使用'A'~'E'。若输出前缀,则前缀输出“0x”,科学计数法中输出’E‘ |
no uppercase * | 十六进制数中使用'a'~'e'。若输出前缀,则前缀输出“0x”,科学计数法中输出’e‘ |
internal | 数值的符号(正负号)在指定宽度内左对齐,数值右对齐,中间由填充字符填充 |
程序7-5 使用流操纵符控制整数输出
#include
#include
using namespace std;
int main()
{
int n = 65535,m = 20;
//1)分别输出一个整数的十进制、十六进制和八进制表示
cout<<"1) "<
程序7-6 使用流操纵符控制浮点数输出
#include
#include
using namespace std;
int main()
{
double x = 1234567.89,y = 1.23456789;
//1)无格式控制时
cout<<"无格式控制时: \t\t 1)x=("<
程序7-7 通过setw()控制输入格式
#include
#include
using namespace std;
int main()
{
string s1,s2;
cin>>setw(5)>>s1>>setw(3)>>s2;
cout<<"s1="<
标志字
为满足不同用户对数据输入/输出格式的要求,C++提供了通过setiosflags()设置标志字进行格式控制的方式。setiosflags()是带参数的操纵符,在头文件iostream中,用以设置指定的标志,函数的参数为流的格式标志位。
表7-6 常见格式标志常量及含义
标志常量名 | 值 | 含义 | 输入/输出 |
---|---|---|---|
ios::skipws | 0X0001 | 跳过输入中的空白 | I |
ios::left | 0X0002 | 按输出域左对齐,用填充字符填充右边 | O |
ios::right * | 0X0004 | 按输出域右对齐,用填充字符填充左边 | O |
ios::internal | 0X0008 | 在符号位或基数指示符后填入字符 | O |
ios::dec * | 0X0010 | 转换为十进制基数形式 | I/O |
ios::oct | 0X0020 | 转换为八进制基数形式 | I/O |
ios::hex | 0X0040 | 转换为十六进制基数形式 | I/O |
ios::showbase | 0X0080 | 在输出中显示基数指示符 | O |
ios::showpoint | 0X00100 | 在浮点数时必须带小数点和尾部的0 | O |
ios::uppercase | 0X00200 | 以大写字母表示十六进制数,科学计数法使用大写字母E | O |
ios::showpos | 0X00400 | 正数前加”+“号 | O |
ios::scientific | 0X00800 | 科学计数法显示浮点数 | O |
ios::fixed | 0X001000 | 定点形式表示浮点数 | O |
ios::unitbuf | 0X002000 | 插入操作后立即刷新流 | O |
程序7-8 通过setiosflags()设置标志字进行格式控制
#include
#include
using namespace std;
int main()
{
double x = 12.34;
cout<<"1)"<
调用cout的成员函数
ostream类提供了在cout中控制输出格式的成员函数,常见的有:
- 设置和返回标志字 long flags(long 1Flags); long flags() const;
- 设置标志位 long setf(long 1Flags);
- 清除标志位 long unsetf(long 1Flags);
- 设置和返回输出宽度 int width(int w); int width() const;
- 设置填充字符 char fill(char cFill); char fill() const;
- 设置数据显示精度 int precision(int np); int precision() const;
表7-7 ostream类的成员函数及与其作用相同的流操纵符
成员函数 | 作用相同的流操纵符 |
---|---|
precision(int np) | setprecision(np) |
widh(int nw) | setw(nw) |
fill(char cFill) | setfill(cFill) |
setf(long IFlags) | setiosflags(IFlags) |
unsetf(long IFlags) | resetiosflags(IFlags) |
程序7-9 使用cout中的函数控制输出格式
#include
using namespace std;
int main()
{
double values[] = {1.23,20.3456,300.4567,4000.56789,50000.1234567};
cout.fill('*'); //设置填充字符为星号*
for(int i = 0;i
ostream类还有一些输出流的成员函数。
- 字符插入 ostream &put(char c);
- 数据块插入 ostream & write(const char *pch,int nCount);
程序7-10 使用cont()函数
#include
using namespace std;
int main()
{
char c = 'a',str[80] = "0123456789abcdefghijklmn";
int x = 65;
cout<<"cout.put('a'): ";cout.put('a');
cout<<"\ncout.put(c+25): ";cout.put(c+25);
cout<<"\ncout.put(x): ";cout.put(x);
cout<<"\ncout.write(str,20): ";
cout.write(str,20);
return 0;
}
cout.put('a'): a
cout.put(c+25): z
cout.put(x): A
cout.write(str,20): 0123456789abcdefghij
调用cin的成员函数
get()函数
从输入流中读取一个字符(包括空白字符),返回值就是该字符的ASCII码。
程序7-11 采用EOF判断输入是否结束
#include
using namespace std;
int main()
{
int n = 0;
char ch;
while((ch = cin.get())!=EOF)
{
cout.put(ch);
n++;
}
cout<<"输入字符共计: "<
getline()函数
从输入流中的当前字符开始读取bufSize-1个字符到缓冲区buf,或读到'\n'为止。
程序7-12 getline()函数功能演示
#include
using namespace std;
int main()
{
char buf[10];
int i = 0;
while(cin.getline(buf,10)) //若输入流的一行超过9个字符,则会报错
cout<<++i<<":"<
eof()函数 bool eof();
用于判断输入流是否已经结束。
ignore()函数 istream & ignore(int n=1,int delim=EOF);
跳过输入流中的n个字符,或跳过delim及其之前的所有字符。
例如,现有若干电话号码。提取号码
Home: 12345678901
Tel:12345678019
Office:19876543210
程序7-13 从输入的字符串中提取电话号码
#include
using namespace std;
int main()
{
char str[30];
while(!cin.eof())
{
cin.ignore(10,':'); //在cin流中跳过':'之前的全部字符
if(!cin.eof())
{
cin>>str;
cout<
peek()函数 int peek();
返回输入流中的当前字符,但是并不将该字符从输入流中取走——相当于只是”看了一眼“将要读入的下一个字符,因此叫”窥视“。
程序7-14 日期格式转换
#include
#include
#include
using namespace std;
string Months[13] = {"","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};//西文格式的月份
int main()
{
char ch;
int year,month,day;
while((ch=cin.peek())!=EOF)
{
if(ch>='A'&&ch<'Z')
{
string sMonth;
cin>>sMonth>>day>>year; //接收:月、日、年
//查找月份完成转换
for(month=0;month<12&&sMonth!=Months[month];++month);
}
else
{
cin>>year;
cin.ignore()>>month;
cin.ignore()>>day;
//以上3条语句等价于:cin>>year>>ch>>month>>ch>>day;
}
cin.ignore(); //跳过\n
cout<