就像C语言一样,C++语言中也没有输入/输出语句。但C++编译系统带有一个面向对象的输入/输出软件包,它就是I/O流类库。流是I/O流类的中心概念。首先介绍流的概念,然后介绍流类库的结构和使用。对于流类库中类的详细说明及类成员的描述,请读者查阅所使用的编译系统的运行库参考手册。
一、I/O流的概念
使用VC++6.0在程序中实现I/O有几种方法:
C运行库直接非缓冲的I/O
ANSI C运行库流I/O
控制台和端口直接I/O
Microsoft Foundation类库
Microsoft I/O流类库
其中I/O流类库对于缓冲的、格式化文本I/O是很有用的,它是C语言中I/O函数在面向对象的程序设计方法中的一个替换产品。
我们简单介绍过,在C++中,将数据从一个对象到另一个对象的流动抽象为“流”。从流中获取数据的操作称为提取操作,向流中添加数据的操作称为插入操作,数据的输入与输出就是通过I/O流来实现的。这里,我们进一步介绍流的概念
操作系统是将键盘、屏幕、打印机和通信端口作为扩充文件来处理的,而这种处理是通过操作系统的设备驱动程序来实现的。因此,从C++程序员的角度来看,这些设备与磁盘文件是等同的。I/O流类就是用来与这些扩充文件进行交互。
当程序与外界环境进行信息交换时,存在着两个对象,一个是程序中的对象,另一个是文件对象。流是一种抽象,它负责在数据的生产者和数据的消费者之间建立联系,并管理数据的流动。程序建立一个流对象,并指定这个流对象与某个文件对象建立连接,程序操作流对象,流对象通过文件系统对所连接的文件对象产生作用。由于流对象是程序中的对象与文件对象进行交换的界面,对程序对象而言,文件对象有的特性,流对象也有,所以程序将流对象看作是文件对象的化身。
流所涉及的范围还远不止于此,凡是数据从一个地方传输到另一个地方的操作都是流的操作。像网络数据交换、进程数据交换等都是流操作。因此,一般意义下的读操作在流数据抽象中被称为(从流中)提取,写操作被称为(向流中)插入。
I/O流类列表
类 名 说 明 包含文件
抽象流基类
ios 流基类 iostream.h
输人流类
fstream 通用输入流类和其他输入流的基类 iostream.h
ifstream 输入文件流类 fstream.h
fstream_withassign cin的输人流类 iostream.h
istrstream 输入字符串流类 strstrea.h
输出流类
ostream 通用输出流类和其他输出流的基类 iostream.h
ofstream 输出文件流类 fstream.h
ostream_withassign cout、cerr和clog的输出漉类 iostream.h
ostrstream 输出字符串流类 strstrea.h
输入/输出流类
iostream 通用输入/输出流类和其他输入/输出流的基类 iostream.h
fstream 输入/输出文件流类 fstream.h
strstream 输人/输出字符串流类 strstrea.h
stdiostream 标准I/0文件的输入/输出类 stdiostr.h
流缓冲区类
streambuf 抽象流缓冲区基类 iostream.h
filebuf 磁盘文件的流缓冲区类 fstream.h
strstreambuf 字符串的流缓冲区类 strstrea.h
stdiobuf 标准I/O文件的流缓冲区类 stdiostr.h
预先定义的流初始化类
iostream-init 预先定义的流初始化类 iostream.h
二、输出流
一个输出流对象是信息流动的目标,最重要的三个输出流是ostream,ofstream和 ostrstream。
ostream类通过派生类ostream_withassign支持预先定义的流对象:
&S226;cout标准输出
&S226;cerr标准错误输出,没有缓冲,发送给它的内容立即被输出。
&S226;clog类似于cerr,但是有缓冲,缓冲区满时被输出。
从ostream和ostream_withassign构造的对象很少,通常用于预先定义的对象。 ostream类可以用于缓冲的或非缓冲的操作,最适合于顺序文本模式输出。
ofstream类支持磁盘文件输出。如果你需要一个只输出的磁盘文件,可以构造一个 ofstream类的对象。在打开文件之前或之后可以指定ofstream对象接受二进制或文本模式数据。很多格式化选项和成员函数可以应用于ofstream对象,包括基类ios和ostream的所有功能。
如果在构造函数中指定一个文件名,当构造这个文件时该文件是自动打开的。否则,你可以在调用缺省构造函数之后使用open成员函数打开文件,或者在一个由文件指示符标识的打开文件基础上构造一个ofstream对象。
(一)构造输出流对象
如果你仅使用预先定义的cout、cerr或clog对象,就不需要构造一个输出流。例如,在本章之前的例题中,我们都是将信息输出到标准输出设备,使用的是cout。如果要使用文件流将信息输出到文件,便需要使用构造函数来建立流对象。
构造输出文件流的常用方法如下:
&S226;使用缺省构造函数,然后调用open成员函数,例如:
ofstream myFile; //声明一个静态输出文件流对象
myFile.open("filename",iosmode), //打开文件,使流对象与文件建立联系
或:
ofstream* pmyFile=new ofstream; //建立一个动态的输出文件流对象
pmyFile->open("filename",iosmode); //打开文件,使流对象与文件建立联系
&S226;在调用构造函数时指定文件名和模式,
ofstream myFile("filename",iosmode);
稍后会详细介绍open成员函数。
(二)使用插入运算符和控制格式
本小节介绍如何控制输出格式以及如何为自己的类建立插入运算符。插入(<<)运算符是所有标准C++数据类型预先设计的,用于传送字节到一个输出流对象。插入运算符与预先定义的操纵符一起工作,用来控制输出格式。
1.输出宽度
为了调整输出,可以通过在流中放人setw操纵符或调用width成员函数为每个项 (item)指定输出宽度。下面的例子在一列中以至少10个字符宽按右对齐方式输出数值:
例 使用width函数控制输出宽度
#i nclude<iostream.h>
void main()
{
double s[]={1.23,35.36,653.7,4358.24};
for(int i=0;i<4;i++)
{
cout.width(10);
cout<<s[i]<<"/n";
}
}
其输出结果是:
1.23
35.26
653.7
4358.24
从程序的输出结果可以看到,在少于10个字符宽的数值前加入了引导空格。
空格是缺省的填充符,当输出的数据不能充满指定的宽度时,系统会自动以空格填充,也可以指定用别的字符来填充。使用fill成员函数可以为已经指定宽度的域设置填充字符的值。为了用星号填充数值列,我们可以将上例中的for循环修改如下:
for(int i=0;i<4;i++)
{
cout.width(10);
cout.fill("*");
cout<<s[i]<<endl;
}
其输出结果如下:
*******1.23
******35.26
*****653.7
****4358.24
如果要为同一行中输出的不同数据项分别指定宽度,可以使用setw操纵符.
例 使用setw操纵符指定宽度
#i nclude<iostream.h>
#i nclude<iomanip.h>
void main()
{
double s[]={1,23,35.36,653.7,4358.24};
char *names[]={"Zoot","Jimmy","Al","Stan"};
for (int i=0;i<4;i++)
cout<<setw(6)<<names[i]<<setw(10)<<s[i]<<endl;
}
width成员函数在iostream.h中说明了,如果带参量使用setw或任何其他操纵符,就必须包括iomanip.h。在输出中,字符串输出在宽度为6的域中,整数输出在宽度为10的域中,运行结果如下:
Zoot 1.23
Jimmy 35.36
Al 653.7
Stan 4358.24
setw和width都不截断数值。如果数值位超过了指定宽度,则显示全部值,当然还要遵守该流的精度设置。setw和width仅影响紧随其后的域,在一个域输出完后域宽度恢复成它的缺省值(必要的宽度)。但其他流格式选项保持有效直到发生改变。
2.对齐方式
输出流缺省为右对齐文本,为了在前面的例子中左对齐姓名和右对齐数值,要将程序修改如下:
例 设置对齐方式
#i nclude<iostream.h>
#i nclude<iomanip.h>
void main()
{
double s[]={1.23,35.36,653.7,4358.24};
char *names[]={"Zoot","Jimmy","Al","Stan"};
for (int i=0;i<4;i++)
cout<<setiosflags(ios::left)
<<setw(6)<<names[i]
<<resetiosflags(ios::left)
<<setw(10)<<s[i]<<endl;
}
其输出结果如下:
Zoot 1.23
Jimmy 35.36
Al 653.7
Stan 4358.24
这个程序中,是通过使用带参数的setiosflags操纵符来设置左对齐,参数是los::left枚举器。该枚举器定义在ios类中,因此引用时必须包括ios::前缀。这里需要用resetiosflags操纵符关闭左对齐标志。setiosflags不同于width和setw,它的影响是持久的,直到用resetiosflags重新恢复缺省值时为止。
setiosflags的参数是该流的格式标志值,这个值由如下位掩码(ios枚举器)指定,并可用位或OR(|)运算符进行组合:
ios::skipws 在输人中跳过空白。
ios::left 左对齐值,用填充字符填充右边。
ios::right 右对齐值;用填充字符填充左边(缺省对齐方式)。
ios::internal 在指定任何引导标记或基之后增加填充字符。
ios::dec 以基10(十进制)格式化数值(缺省进制)。
ios::oct 以基8(八进制)格式化数值。
ios::hex 以基16(十六进制)格式化数值。
ios::showbase 以C++编译器能读的格式显示数值常量。
ios::showpoint 对浮点数值显示小数点和尾部的0。
ios::uppercase 对于十六进制数值显示大写字母A到F,对于科学格式显示大写字母E。
ios::showpos 对于正数显示正号(+)。
ios::scientific 以科学格式显示浮点数值。
ios::fixed 以定点格式显示浮点数值。
ios::unitbuf 导致在每次插入之后ostream::osfx刷新该流。缺省地,cerr是缓冲的单元。
ios::stdio 导致在每次插入之后ostream::osfx刷新该流的stdout和stderr。
3.精度
浮点数输出精度的缺省值是6,例如,数3466.9768显示为3466.98。为了改变精度,可以使用setprecision操纵符,该操纵符有两个标志,ios::fixed和los::scientific。如果设置了ios::fixed,该数输出为3466.976800;如果设置了ios::scientific,该数输出为 3.4669773+003。为了以1位有效数字显示浮点数,将前述程序修改如下:
例 控制输出精度
#i nclude<iostream.h>
#i nclude<iomanip.h>
void main()
{
double s[]={1.23,35.36,653.7,4358.24};
char* names[]={"Zoot","Jimmy","Al","Stan"};
for(int i=0;i<4;i++)
cout<<setiosflags(ios::left)
<<setw(6)<<names[i]
<<resetiosflags(ios::left)
<<setw(10)<<setprecision(1)<<s[i]<<endl;
}
该程序的输出结果如下:
Zoot 1
Jimmy 4e+001
Al 7e+002
Stan 5e+003
如果要删除科学标记,需要在for循环之前插入语句: cout<<setiosflags(ios::fixed);如果要采用定点标记,该程序用一个小数位输出的结果如下:
Zoot 1.2
Jimmy 35.4
Al 653.7
Stan 4358.2
如果改变ios::fixed为ios::scientific,该程序的输出结果为:
Zoot 1.2e+000
Jimmy 3.5e+001
Al 6.5e+002
Stan 4.4e+003
同样,该程序在小数点后输出了一位数字,这表明如果设置了ios::fixed或 ios::scientific,则精度值确定了小数点之后的小数位数。如果都未设置,则精度值确定了总的有效位数。可以用resetiosflags操纵符清除这些标志。
4.进制
dec、oct和hex操纵符设置输入和输出的缺省进制,例如,若将hex操纵符插入到输出流中,则以十六进制格式输出。如果ios::uppercase(缺省)标志已清除,该数值以a到f的数字显示;否则,以大写方式显示。缺省的进制是dec(十进制)。