fstreamifstreamofstream 详解与用法.docx
《fstreamifstreamofstream 详解与用法.docx》由会员分享,可在线阅读,更多相关《fstreamifstreamofstream 详解与用法.docx(14页珍藏版)》请在冰豆网上搜索。
fstreamifstreamofstream详解与用法
fstream,ifstream,ofstream详解与用法
fstream,istream,ofstream三个类之间的继承关系
fstream:
(fstream继承自istream和ofstream)
1.typedef basic_fstream> fstream;//可以看出fstream就是basic_fstream
2.templateclass basic_fstream:
publicbasic_iostream<_Elem,_Traits>
3.templateclass basic_iostream:
publicbasic_istream<_Elem,_Traits>,publicbasic_ostream<_Elem,_Traits>
istream:
1.typedef basic_ifstream> ifstream;//basic_ifstream就是istream
2.templateclass basic_ifstream :
publicbasic_istream<_Elem,_Traits>//basic_istream其实就是istream
3.templateclass basic_istream :
virtualpublicbasic_ios<_Elem,_Traits>
4.templateclass basic_ios :
public ios_base
5.class_CRTIMP2_PURE ios_base :
public _Iosb//最终父类
ofstream:
1.typedef basic_ofstream> ofstream;
2.templateclass basic_ofstream :
publicbasic_ostream<_Elem,_Traits> //basic_ostream其实就是ostream
3.templateclass basic_ostream:
virtualpublicbasic_ios<_Elem,_Traits>
4.templateclass basic_ios :
public ios_base
5.class_CRTIMP2_PURE ios_base :
public _Iosb//最终父类
这里顺道也把STL中其他几个iostreams提一句
iostream:
1.typedef basic_iostream> iostream;
2.templateclass basic_iostream:
publicbasic_istream<_Elem,_Traits>,publicbasic_ostream<_Elem,_Traits>
3.basic_ostream/basic_istream都继承自basic_ios
4.templateclass basic_ios :
public ios_base
5.class_CRTIMP2_PURE ios_base :
public _Iosb//最终父类
strstream //继承自iostream
streambuf //继承自basic_streambuf
stringstream //继承自basic_stringstream
istringstream //继承自basic_istringstream
ostringstream //继承自basic_istringstream
fstream中seekg和seekp是联动的,移动读指针,写指针随之移动,移动写指针,读指针也会随之移动。
为了搞清楚具体是怎么联动的,编了个小程序测试了一下:
intmain()
{
fstreamf("seekpg",ios:
:
in|ios:
:
out);
f<<"aaaaaa"< intpp=f.tellp();
intpg=f.tellg();
cout<<"pp="< f<<"bbbbbbb"< pp=f.tellp();
pg=f.tellg();
cout<<"pp="< f.seekg(3,ios:
:
beg);
pp=f.tellp();
pg=f.tellg();
cout<<"pp="< f.seekp(9,ios:
:
beg);
f<<"cccccc"< pp=f.tellp();
pg=f.tellg();
cout<<"pp="<
return0;
}
程序输出:
pp=7 pg=7
pp=15 pg=15
pp=3 pg=3
pp=16 pg=16
从结果可以发现
1)确实seekg影响到了写操作,seekp也影响到了读操作,seekg和seekp是联动的
2)tellg和tellp的值始终是一样的。
问:
fstream中真有所谓的读指针和写指针吗?
如果有,他们是一个东西吗?
答:
实际上我们知道fstream继承自ifstream和ofstream是他们俩的子类,而seekp和tellp是ofstream的成员函数,seekg和tellg是ifstream的成员函数,seekp是指seekput,seekg是指seekget。
之所以在fstream中他们相同,是因为这里指定打开方式in|out
下面内容转自:
C++通过以下几个类支持文件的输入输出:
∙ofstream:
写操作(输出)的文件类(由ostream引申而来)
∙ifstream:
读操作(输入)的文件类(由istream引申而来)
∙fstream:
可同时读写操作的文件类(由iostream引申而来)
打开文件(Openafile)
对这些类的一个对象所做的第一个操作通常就是将它和一个真正的文件联系起来,也就是说打开一个文件。
被打开的文件在程序中由一个流对象(streamobject)来表示(这些类的一个实例),而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。
要通过一个流对象打开一个文件,我们使用它的成员函数open():
voidopen(constchar*filename,openmodemode);
这里filename是一个字符串,代表要打开的文件名,mode是以下标志符的一个组合:
这些标识符可以被组合使用,中间以”或”操作符(|)间隔。
例如,如果我们想要以二进制方式打开文件"example.bin"来写入一些数据,我们可以通过以下方式调用成员函数open()来实现:
ios:
:
in
为输入(读)而打开文件
ios:
:
out
为输出(写)而打开文件
ios:
:
ate
初始位置:
文件尾
ios:
:
app
所有输出附加在文件末尾
ios:
:
trunc
如果文件已存在则先删除该文件内容
ios:
:
binary
二进制方式
ios:
:
nocreate
不建立文件,所以文件不存在时打开失败
ios:
:
noreplace
不覆盖文件,所以打开文件时如果文件存在失败
ofstreamfile;
file.open("example.bin",ios:
:
out|ios:
:
app|ios:
:
binary);
ofstream,ifstream和fstream所有这些类的成员函数open都包含了一个默认打开文件的方式,这三个类的默认方式各不相同:
类
参数的默认方式
ofstream
ios:
:
out|ios:
:
trunc
ifstream
ios:
:
in
fstream
ios:
:
in|ios:
:
out
ofstream:
打开文件不存在,默认会创建这个文件。
(除非指定ios:
:
nocreate)
ifstream:
打开文件存在与否,默认不会创建在个文件.
fstream:
打开文件不存在,默认会创建这个文件。
(除非只是指定ios:
:
in或者指定ios:
:
nocreate)
只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。
如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合。
由于对类ofstream,ifstream和fstream的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open函数,并拥有同样的参数。
这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:
ofstreamfile("example.bin",ios:
:
out|ios:
:
app|ios:
:
binary);
两种打开文件的方式都是正确的。
你可以通过调用成员函数is_open()来检查一个文件是否已经被顺利的打开了:
boolis_open();
它返回一个布尔(bool)值,为真(true)代表文件已经被顺利打开,假(false)则相反。
关闭文件(Closingafile)
当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。
关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。
它的格式很简单:
voidclose();
这个函数一旦被调用,原先的流对象(streamobject)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。
为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close。
文本文件(Textmodefiles)
类ofstream,ifstream和fstream是分别从ostream,istream和iostream中引申而来的。
这就是为什么fstream的对象可以使用其父类的成员来访问数据。
一般来说,我们将使用这些类与同控制台(console)交互同样的成员函数(cin和cout)来进行输入输出。
如下面的例题所示,我们使用重载的插入操作符<<:
//writingonatextfile
#include
intmain(){
ofstreamexamplefile("example.txt");
if(examplefile.is_open()){
examplefile<<"Thisisaline.\n";
examplefile<<"Thisisanotherline.\n";
examplefile.close();
}
return0;
}
fileexample.txt
Thisisaline.
Thisisanotherline.
从文件中读入数据也可以用与cin的使用同样的方法:
//readingatextfile
#include
#include
#include
intmain(){
charbuffer[256];
ifstreamexamplefile("example.txt");
if(!
examplefile.is_open())
{cout<<"Erroropeningfile";exit
(1);}
while(!
examplefile.eof()){
examplefile.getline(buffer,100);
cout<}
return0;
}
Thisisaline.
Thisisanotherline.
上面的例子读入一个文本文件的内容,然后将它打印到屏幕上。
注意我们使用了一个新的成员函数叫做eof,它是ifstream从类ios中继承过来的,当到达文件末尾时返回true。
状态标志符的验证(Verificationofstateflags)
除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):
∙bad()
如果在读写过程中出错,返回true。
例如:
当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
∙fail()
除了与bad()同样的情况下会返回true以外,加上格式错误时也返回true,例如当想要读入一个整数,而获得了一个字母的时候。
∙eof()
如果读文件到达文件末尾,返回true。
∙good()
这是最通用的:
如果调用以上任何一个函数返回true的话,此函数返回false。
要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。
获得和设置流指针(getandputstreampointers)
所有输入/输出流对象(i/ostreamsobjects)都有至少一个流指针:
∙ifstream,类似istream,有一个被称为getpointer的指针,指向下一个将被读取的元素。
∙ofstream,类似ostream,有一个指针putpointer,指向写入下一个元素的位置。
∙fstream,类似iostream,同时继承了get和put
我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:
∙tellg()和tellp()
这两个成员函数不用传入参数,返回pos_type类型的值(根据ANSI-C++标准),就是一个整数,代表当前get流指针的位置(用tellg)或put流指针的位置(用tellp).
∙seekg()和seekp()
这对函数分别用来改变流指针get和put的位置。
两个函数都被重载为两种不同的原型:
seekg(pos_typeposition);
seekp(pos_typeposition);
使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。
要求传入的参数类型与函数tellg和tellp的返回值类型相同。
seekg(off_typeoffset,seekdirdirection);
seekp(off_typeoffset,seekdirdirection);
使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。
它可以是:
ios:
:
beg
从流开始位置计算的位移
ios:
:
cur
从流指针当前位置开始计算的位移
ios:
:
end
从流末尾处开始计算的位移
流指针get和put的值对文本文件(textfile)和二进制文件(binaryfile)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。
由于这个原因,建议对以文本文件模式打开的文件总是使用seekg和seekp的第一种原型,而且不要对tellg或tellp的返回值进行修改。
对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。
以下例子使用这些函数来获得一个二进制文件的大小:
//obtainingfilesize
#include
#include
constchar*filename="example.txt";
intmain(){
longl,m;
ifstreamfile(filename,ios:
:
in|ios:
:
binary);
l=file.tellg();
file.seekg(0,ios:
:
end);
m=file.tellg();
file.close();
cout<<"sizeof"<cout<<"is"<<(m-l)<<"bytes.\n";
return0;
}
sizeofexample.txtis40bytes.
二进制文件(Binaryfiles)
在二进制文件中,使用<<和>>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。
文件流包括两个为顺序读写数据特殊设计的成员函数:
write和read。
第一个函数(write)是ostream的一个成员函数,都是被ofstream所继承。
而read是istream的一个成员函数,被ifstream所继承。
类fstream的对象同时拥有这两个函数。
它们的原型是:
write(char*buffer,streamsizesize);
read(char*buffer,streamsizesize);
这里buffer是一块内存的地址,用来存储或读出数据。
参数size是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。
//readingbinaryfile
#include
#include
constchar*filename="example.txt";
intmain(){
char*buffer;
longsize;
ifstreamfile(filename,ios:
:
in|ios:
:
binary|ios:
:
ate);
size=file.tellg();
file.seekg(0,ios:
:
beg);
buffer=newchar[size];
file.read(buffer,size);
file.close();
cout<<"thecompletefileisinabuffer";
delete[]buffer;
return0;
}
Thecompletefileisinabuffer
缓存和同步(BuffersandSynchronization)
当我们对文件流进行操作的时候,它们与一个streambuf类型的缓存(buffer)联系在一起。
这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。
例如,对于一个输出流,每次成员函数put(写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。
当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。
这个过程称为同步(synchronization),它会在以下任一情况下发生:
∙当文件被关闭时:
在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。
∙当缓存buffer满时:
缓存Buffers有一定的空间限制。
当缓存满时,它会被自动同步。
∙控制符明确指明:
当遇到流中某些特定的控制符时,同步会发生。
这些控制符包括:
flush和endl。
∙明确调用函数sync():
调用成员函数sync()(无参数)可以引发立即同步。
这个函数返回一个int值,等于-1表示流没有联系的缓存或操作失败。
∙在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
1、插入器(<<)
向流输出数据。
比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"WriteStdout"<<'n';就表示把字符串"WriteStdout"和换行字符('n')输出到标准输出流。
2、析取器(>>)
从流中输入数据。
比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
在C++中,对文件的操作是通过stream的子类fstream(filestream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。
下面就把此类的文件操作过程一一道来。
一、打开文件
在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
voidopen(constchar*filename,intmode,intaccess);
参数:
filename:
要打开的文件名
mode:
要打开文件的方式
access:
打开文件的属性
打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:
ios:
:
app:
以追加的方式打开文件
ios:
:
ate:
文件打开后定位到文件尾,ios:
app就包含有此属性
ios:
:
binary:
以二进制方式打开文件,缺省的方式是文本方式。