(三)输出文件流成员函数
输出流成员函数有三种类型:
&S226;与操纵符等价的成员函数。
&S226;执行非格式化写操作的成员函数。
&S226;其他修改流状态且不同于操纵符或插入运算符的成员函数。
对于顺序的格式化输出,可以仅使用插入运算符和操纵符。对于随机访问二进制磁盘输出,使用其他成员函数,使用或不使用插入运算符。
1.输出流的open函数
要使用一个输出文件流(ofstream),必须在构造函数或open函数中把该流与一个特定的磁盘文件关联起来。在各种情况下,描述文件的参量是相同的。
当你打开一个与输出流关联.的文件时,通常指定一个open_mode标志,如下表所示。可以用按位OR(|)运算符组合这些标志,它们作为枚举器定义在ios类中。
表 输出文件流文件打开模式
标 志 功 能
ios::app 打开一个输出文件用于在文件尾添加数据
ios::ate 打开一个现存文件(用于输入或输出)并查找到结尾
ios::in 打开一个输入文件对于一个ofstream文件,使用ios::in作为一个openmode可避免删除一个现存文件中现有的内容 ios::out 打开二个文件,用于输出。对于所有ofstream对象,此模式是隐含指定的 ios::nocreate 如果一个文件存在则打开它,否则该操作失败 i。
s::noreplace 如果一个文件不存在则作为新文件打开它,如果文件已存在,则该操作失败
ios::trunc 打开一个文件,如果它已经存在则删除其中原有的内容如果指定了ios::out,但没有指定iOs::ate、iOs::app和ios::in,则隐含为此模式
ios::binary 以二进制模式打开一个文件(缺省是文本模式)
有三个公共输出流涉及模式选项:
&S226;建立一个文件,如果该文件己存在,则删除旧的版本:
ostream ofile("FILENAME");//缺省模式是ios::out
ofstream ofile("FILENAME",ios::out);//与上一句等效
&S226;添加记录到一个现存文件中,如果文件不存在便建立一个新文件:
ofstream ofile(“FiILENAME”,ios::app);
&S226;使用同一个流先后打开不同的文件(在同一时刻只有一个是打开的):
ofstream ofile();
ofile.open("FTILE1",ios::in) //打开文件FILEl
…// 向文件FILEl
输出 ofile.close(); //关闭FILEl
ofile.open("FILE2",ios::in); //打开文件FILE2
…// 向文件FILE2输出
ofile.close(); //关闭FILE2
//当对象ofile离开它的作用域时便消亡
2.put函数
put函数把一个字符写到输出流中,下面两个语句缺省是相同的,但第二个受该流的格式化参量的影响:
cout.put(‘A’); //精确地输出一个字符
cout<<"A"; //输出一个字符,但此前设置的宽度和填充方式在此起作用
3.write函数
write函数把一个内存中的一块内容写到一个输出文件流中,长度参数指出写的字节数。下面的例子建立一个输出文件流并将Date结构的二进制值写入文件:
例 向文件输出
#i nclude<fstream.h>
struct Date
{
int mo,da,yr;
};
void main()
{
Date dt={6,10,92};
ofstream tfile("date.dat",ios::binary);
tfile.write((char*)&dt,sizeof dt);
}
write函数当遇到空字符时并不停止,因此能够写入完整的类结构,该函数带两个参量,一个char指针(指向内存数据的起始地址)和一个所写的字节数。注意在该结构对象的地址之前需要char*做强制类型转换。
4.seekp和tellp函数
一个输出文件流保存一个内部指针指出下一次写数据的位置。seekp成员函数设置这个指针,因此可以以随机方式向磁盘文件输出。tellp成员函数返回该文件位置指针值。
5.输出流的close函数
close成员函数关闭与一个输出文件流关联的磁盘文件。文件使用完毕后必须将其关闭以完成所有磁盘输出。虽然ofstream析构函数(destructor)会自动完成关闭,但如果需要在同一流对象上打开另外的文件,就需要使用close函数。
如果构造函数或open成员函数打开了该文件,输出流析构函数自动关闭一个流的文件。
6.错误处理函数
错误处理成员函数的作用是在写到一个流时进行错误处理。各函数及其功能如下表所示。
错误处理成员函数及其功能
函 数 功能及返回值
bad 如果出现一个不可恢复的错误,则返回一个非0值
fail 如果出现一个不可恢复的错误或一个预期的条件,例如一个转换错误或文件未找到,用返回一个非0值。在用零参量调用clear之后可能经常恢复处理
good 如果没有错误条件(不可恢复的或其他)和没有设置文件结尾标志,则返回一个非0值
eof 遇到文件结尾条件,则返回一个非0值
clear 设置内部错误状态,如果用缺省参量调用,则清除所有错误位
rdstate 返回当前错误状态
! 运算符经过了重载,它与fail函数执行相同的功能,因此表达式if(! cout)等价于 if (cout.fail())。
void*()运算符也是经过重载的,与!运算符相反,因此表达式if(cout)等价于if(! cout.fail())。
void*()运算符不等价于good,因为它不检测文件结尾。
(四)二进制输出文件
最初设计流的目的是用于文本,因此缺省的输出模式是文本方式。在以文本模式输出时,若遇到换行符(十进制10),便自动被扩充为回车换行符(十进制13)。这种自动扩充有时可能出问题,请看下列程序:
#i nclude<fstream.h>
int iarray[2]={99,10};
void main()
{
ofstream os("test.dat");
os.write((char *)iarray,sizeof(iarray));
}
当执行程序,向文件中输出时,10会被自动转换成13,然而这里的转换显然不是我们需要的。要想解决这一问题,就要采用二进制模式输出。使用二进制模式输出时,其中所写的字符是不转换的。使用二进制模式输出到文件有下列几种方法,
(1)以通常方式构造一个流,然后使用setmode成员函数,在文件打开后改变模式,例如:
ofstream ofs("test.dat");
ofs.setmode(filebuf::binary);
ofs.write((char*)iarray,4); //向二进制文件中写入4字节数据
(2)使用ofstream构造函数中的模式参量指定二进制输出模式,例如:
#i nclude<fstream.h>
#i nclude<fcntl.h>
#i nclude<io.h>
int iarray[2]={99,10};
void main()
{
ofstream ofs("test.dat",ios::binary);
ofs.write((char*)iarray,4); //向二进制文件中写入4字节数据
}
(3)使用二进制操作符代替setmode成员函数:
ofs<<binary;
若使用text操作符便把流切换到文本转换模式。
(4)使用open函数带一个二进制模式标志打开文件,例如:
filedesc fd=open("test.dat",OBINARY|OCREAT|OWRONLY);
ofstream ofs(fd);
ofs.write((char*)iarray,4); //向二进制文件中写入4字节数据
三、输入流
一个输入流对象是数据流出的源头,三个最重要的输人流类是istream,ifstream和 istrstream。
istream类最适合用于顺序文本模式输入。基类ios的所有功能都包括在istream中。我们很少需要从类istream构造对象,通常使用预先定义的cin对象,它实际上是 istream_withassign类的一个对象,ifstream类支持磁盘文件输入。如果需要一个仅用于输入的磁盘文件,可以构造一个 ifstream类的对象,并且可以指定使用二进制或文本模式。如果在构造函数中指定一个文件名,在构造该对象时该文件便自动打开。否则,需要在调用缺省构造函数之后使用open函数来打开文件。很多格式化选项和成员函数都可以应用于ifstream对象,基类ios和 istream的所有功能都包括在ifstream中。
(一)构造输入流对象
如果仅使用cin对象,则不需要构造输入流对象。如果要使用文件流从文件中读取数据,就必须构造一个输入流对象。建立一个输入文件流的常用方式如下:
(1)使用缺省构造函数建立对象,然后调用open成员函数打开文件,例如:
ifstream myFile; //建立一个文件流对象
myFile.open("filename",iosmode); //打开文件mename
或:
ifstream *pmyFile=new ifstream; //动态建立一个文件流对象,获得对象指针
pmyFile->open("filename",iosmode); //用对象指针调用open函数打开文件
(2)在调用构造函数建立文件流对象时指定文件名和模式,在构造过程中打开该文件:
ifstream myFile("filename",iosmode);
(二)使用提取运算符
提取(extraction)运算符(>>)对于所有标准C++数据类型都是预先设计好的,它是从一个输入流对象获取字节最容易的方法。
提取运算符(>>)是用于格式化文本输入的,在提取数据时,以空白符为分隔。如果要输入一段包含空白符的文本,用提取运算符就很不方便。在这种情况下,可以选择使用。非格式化输入成员函数getline,这样就可以读一个包含有空格的文本块,然后再对其进行分析。另一种方法是派生一个输入流类,带有一个成员函数如GetNextToken,它调用 istream成员提取和格式化字符数据。
前面介绍过的输出流错误处理函数同样可应用于输人流。在提取中测试错误是重要的。
(三)输入流操纵符
很多操纵符,如setprecision都定义在ios类中,因此可以应用于输人流。但是只有少数几个操纵符对输人流对象具有实际影响,其中最重要的是进制操纵符dec、oct和hex。
在提取中,hex操纵符可以接收处理各种输入流格式,例如c、C、0xc、0xC、0Xc和 0XC都将被解释为十进制数12。任何除0到9、A到F、a到f和X之外的字符都将引起数值变换终止。例如,序列124n5将变换成数值124,并设置ios::fail位。
(四)输入流成员函数
输人流成员函数用于从磁盘文件中输入,这些成员函数包括:
&S226;open函数
&S226;get函数
&S226;getline函数
&S226;read函数
&S226;seekg和tellg函数
&S226;close函数
1.输入流的open函数
如果要使用一个输入文件流(ifstream),必须在构造函数中或者使用open函数把该流与一个特定磁盘文件关联起来。无论哪种方式,参量是相同的。
当打开与一个输入流关联的文件时,通常要指定一个模式标志。模式标志如下表所示,该标志可以用按位OR(|)运算符进行组合。
表 输入文件流文件打开模式
ios::in 打开文件用于输入(缺省)
ios::nocreate 如果文件不存在,该函数失败
ios::binary 以二进制模式(缺省模式是文本模式)打开文件
注意:如果需要测试文件是否存在,必须在打开文件时指定ios::nocreate模式,然后使用fail成员函数确定:
istream ifile("FILENAME",ios::nocreate);
if (ifile.fail())
//The file does not exist...
2.get函数
非格式化get函数的功能与提取运算符(>>)很相像,主要的不同点是get函数在读人数据时包括空白字符,而提取运算符在缺省情况下拒绝接受空白字符。
例 get函数应用举例
#i nclude<iostream.h>
void main()
{
char ch;
while((ch=cin.get())!=EOF)
cout.put(ch);
}
运行时如果输入:
abc xyz 123
则输出:
abc xyz 123
3.getline函数
getline成员函数的功能是允许从输人流中读取多个字符,并且允许指定输入终止字符(缺省值是换行字符),在读取完成后,从读取的内容中删除该终止字符。
例 为输入流指定一个终止字符
本程序连续读人一串字符,直到遇到字符‘t’时停止,字符个数最多不超过99个。
#i nclude<iostream.h>
void main()
{
char line[100];
cout<<"Type a 1ine terminated by "t""<<endl;
cin.getline(1ine,100,"t");
cout<<line;
}
4.read函数
read成员函数从一个文件读字节到一个指定的存储器区域,由长度参数确定要读的字节数。如果给出长度参数,当遇到文件结束或者在文本模式文件中遇到文件结束标记字符时读结束。
例 从一个payroll文件读一个二进制记录到一个结构中
#i nclude<io.h>
void main()
{
struct
{
double salary;
char name[23];
}employee;
ifstream is("payroll",ios::binary|ios::nocreate);
if (is)
{
is.read((char*)&employee,sizeof(employee));
cout<<employee.name<<" "<<employee.salary<<endl;
}
else
{
cout<<"ERROR:Cannot openfile"payroll"."<<endl;
}
}
这里假设数据记录是通过指定的结构严格格式化的,并且设有终止的回车或换行字符。
5.seekg和tellg函数
在输入文件流中,保留着一个指向文件中下一个将读数据的位置的内部指针,可以用 seekg函数来设置这个指针。
例 用seekg函数设置位置指针
#i nclude<fstream.h>
void main()
{
char ch;
ifstream tfile("payroll",ios::binary | ios::nocreate);
if(tfile)
{
tfile.seekg(8);
while(tfile.good())
{
//遇到文件结束或读取操作失败时结束读操作
tfile.get(ch);
if (!ch)break; //如果没有读到则退出循环
cout<<ch;
}
}
else
{
cout<<"ERROR;Cannot open file "payroll"."<<endl;
}
}
使用seekg可以实现面向记录的数据管理系统,用固定长度的记录尺寸乘以记录号便得到相对于文件末尾的字节位置,然后使用get读这个记录。
tellg成员函数返回当前文件读指针的位置,这个值是streampos类型,该typedef结构定义在iostream.h中。
例 读一个文件并显示出其中空格的位置
#i nclude<fstream.h>
void main()
{
char ch;
ifstream tfile("payroll",ios::binary | ios::nocreate);
if (tfile)
{
while(tfile.good())
{
streampos here=tfile.tellg();
tfile.get(ch);
if(ch==" ")
cout<<"/nPosition"<<here<<"is a space";
}
}
else
{
cout<<"ERROR:Cannot open file "payroll"."<<endl;
}
}
6.输入流的close函数
close成员函数关闭与一个输入文件流关联的磁盘文件。
虽然ifstream类的析构函数可以自动关闭文件,但是如果需要使用同一流对象打开另一文件,则首先要用close函数关闭当前文件。
四、输入/输出流
一个iostream对象可以是数据的源或目的。两个重要的I/O流类都是从iostream派生的,它们是fstream和strstream。这些类继承了前面描述的stream和ostream类的功能。
fstream类支持磁盘文件的输入和输出。如果你需要在同一个程序中从一个特定磁盘文件读和写到该磁盘文件,可以构造一个fstream对象。一个istream对象是有两个逻辑子流的单个流,两个子流一个用于输入,另一个用于输出。详细说明请读者参考联机帮助或运行库参考手册。