from http://wanglimin2004.blog.163.com/blog/static/115488498201203131150203/
1. 简介
C++标准库有丰富的管理磁盘文件的I/O类。这些类构成了一个类的层次结构,它有一个通用的顶层基类和一些底层的具体的派生类,这些派生类可以通过流保存和加载各种磁盘文件数据。所谓的“流”是一系列字节,它可以从一种类型的设备流向另一种类型的设备。就文件流来说,通过流连接起来的两个设备是计算机的内存和磁盘上的文件。
2. 类std::ofstream
如果程序员只需要执行磁盘文件输出,那么可以创建类std::ofstream的对象来处理I/O任务。下面的示例程序使用了最简单形式的std::ofstream对象,创建了一个文件并向文件中写入一个字符串。
#include <fstream>
int main(){
std::cout<<"Creating file..."<<std::endl;
std::ofstream tfile("test.dat");
std::cout<<"Write to file..."<<std::endl;
tfile<<"These are test data";
return 0;
}
类std::ofstream还可以向现有的文件追加内容,在打开文件的时候使用了std::ios::app标志,该标志是模式标志,它告诉tfile对象以追加模式打开文件。该模式会保留文件中的现有数据内容,示例程序:
#include <fstream>
int main(){
std::cout<<"Opening file..."<<std::endl;
std::ofstream tfile("test.dat",std::ios::app);
std::cout<<"Write to file..."<<std::endl;
tfile<<", and these are more";
return 0;
}
3. 流模式标志
------------------------------------------------------------------------------------------
标志 描述
------------------------------------------------------------------------------------------
ios::app 将所有新数据写到入到文件的末尾
ios::ate 把所有新数据写入到文件的尾部,如果程序移动了文件指针,这时就把数据写入到当前的位置
ios::binary 以二进制模式而不是文本模式打开文件
ios::out 打开用于输出的文件,删除文件的当前内容。该模式只在没有指定文件打开模式时使用
ios::trunc 打开用于输出的文件,删除文件现有内容(是ios::out的默认操作)
ios::nocreate 只在文件存在时打开文件,否则打开文件失败
ios::noreplace 打开一个不存在的文件;如果文件已经存在,文件打开操作失败
------------------------------------------------------------------------------------------
总共有七种标志和两种类型的对象(即类型std::ofstream和std::ifstream),因此可以有许多可能的文件打开模式。这些模式并非都是可以逻辑组合的,对每一种模式来说,所要打开的文件要么存在要么不存在。
最常用的集中情况:
创建一个文件,如果文件已经存在,删除旧的文件:std::ofstream ofile("FILENAME");//no open_mode
读取现有的文件,该文件不存在时报错:std::ifstream ifle("FILENAME");///no open_mode
读取和写入某个现有的文件,这是更新模式,程序可以读取记录、定位记录、重写记录内容和向文件尾部添加记录:std::ifstream ifile("FILENAME",ios::in|ios::out);
向现有的文件中写入数据而不先删除该文件:std::ofstream ofile("FILENAME",ios::out|ios::nocreate);
向现有文件添加记录,如果文件不存在则创建文件:std::ofstream ofile("FILENAME",ios::out|ios::app);
4. 文件流成员函数
类std::ofstream(以及其他的文件流类)有许多用于管理磁盘文件的成员函数,其中的某些成员函数就是在类本身中定义。
------------------------------------------------------------------------------------------
函数 描述
------------------------------------------------------------------------------------------
attach() 把打开的文件与流关联
close() 在刷新未保存的数据后关闭文件
flush() 刷新流
open() 打开一个文件并把它与流关联
put() 向流中写入一个字节
rdbuf() 返回与流关联的filebuf对象
seekp() 设置流文件指针的位置
setmode() 把流设置成二进制模式或者文本模式
tellp() 获取流文件指针的位置
write() 向流中写入一组字节
------------------------------------------------------------------------------------------
5. 类ifstream
类std::ifstream与类std::ofstream是匹配的一对输入/输出类。在只需要从文件读取数据时那就在程序中创建类std::ifstream的一个对象。如下面的程序把文件数据读入到结构对象中,然后用该结构中的数据成员实例化了一个Date对象。
#include <fstream>
#include <Date.h>
int main(){
struct date
{
int mo,da,yr;
}dt;
std::ifstream tfile("date.dat");
tfile.read(reinterpret_case<char*>(&dt),sizeof(dt));
Date dat(dt.mo,dt,da,dt,yr);
std::cout<<dat;//符号重载
return 0;
}
6. 读取文件直至文件结束
在从文件中读取数据时,程序通常需要知道何时到达了文件结尾,类std::ifstream为此提供了成员函数eof()。
#include <fstream>
int main(){
std::ifstream tfile("test.dat");
while(!tfile.eof()){
char ch;
tfile.get(ch);
if(!tfile.eof())
std::cout<<ch;
}
return 0;
}
7. 在文件中定位
ifstream流类提供了一个名为seekg()的成员函数,它能够使程序把文件指针在文件内移动任何字节数(输入修改位置),然后读取数据直到文件结尾。
#include <fstream>
int main(){
std::ifstream tfile("test.dat");
tfile.seekg(6);
while(!tfile.eof()){
char ch;
tfile.get(ch);
........
在函数调用时添加一个参数可以使seekg()或seekp()操作发生在相对于文件起始位置、结束位置或当前位置处。类ios中定义了该参数。下面是一些范例:tfile.seekg(5,ios::beg); tfile.seekg(10,ios::cur); tfile.seekg(-15,ios::end);如果不提供第二个参数,那么操作位置发生相对于文件起始位置处。成员函数tellg()可以确定输入的当前位置,tellp()可以确定输出的当前位置。
8. 读取和写入流文件
我们经常需要打开一个用于读/写访问的文件,这时只要给std::ifstream的构造函数参数表添加一个打开模式(openmode)参数就可以了。打开模式覆盖了std::ifstream的默认的只读模式。如下面的程序示例:
#include <fstream>
#include <cctype>
int main(){
char* fname="test.dat";
std::ifstream tfile(fname,std::ios::in|std::ios::out|std::ios::binary);
std::ostream ofile(tfile.rdbuf());
char tdata[100];
int i=0;
while(!tfile.eof()&&i<sizeof(tdata))
tfile.get(tdata[i++]);
ofile.seekp(0,ios::end);
ofile<<"\r\n";
for(int j=0;j<i-1;j++)
ofile.put(static_case<char>(toupper(tdata[j])));
return 0;
}
9. 文件与流的关联与解除关联
用单个流对象在不同的时候代表不同的文件时不可能的。要做到这一点,在构造流对象时不要提供与流关联的文件名,然后调用流对象的open()成员函数把该流与某个文件关联以及调用成员函数close()解除流与文件的关联。程序示例:
#include <fstream>
int main(){
std::ofstream tfile;
std::cout<<"Creating the test1.dat file..."<<std::endl;
tfile.open("test1.dat");
tfile<<"This is TEST1";
tfile.close();
std::cout<<"Creating the test2.dat file..."<<std::endl;
tfile.open("test2.dat");
tfile<<"This is TEST2";
tfile.close();
return 0;
}
类iostream包含了返回值为TRUE或FALSE的转换函数,因此对象名可用在真/假条件表达式中。如
std::fstream tfile;//No file name given
if(tfile)//This test return false
.....
10. 二进制文件与文本文件
在某些操作平台上(如UNIX),二进制文件和文本文件没有区别,参数std::ios::binary没有效果。其他一些平台(如MS-Dos、Windows)对这两文件是有区分的,表现在:
A.当程序向二进制文件写入一个新行符('\n')时,文件系统写入一个字符。在大多数系统上,新行符等同于回行符(0x0a)
B.当程序把新行符写入文本文件时,文件系统写入两个字符,即先后写入回行符(0x0a)和回车字符(0x0d)
C.当程序从二进制文件读取一个新行符时,文件系统把单个字符读入内存
D.当程序从文本文件读取匹配的回车符/回行符时,文件系统把这一对字符转换成一个新行符并读入内存
E.当程序从文本文件读取一个新行符(前面没有回车符的回行符)时,文件系统把该新行符插入到内存中
这种差异在涉及文件位置操作(查找给定的位置和检索当前位置)时有重大意义。由于存在单个新行符和一对回车符/回行符之间的双向转换,文本文件在内存中的数据表示与其在磁盘中的数据表示长度是不同的。因此,在文本文件中查找给定的位置和检索当前位置是不可靠的。
11. 类std::fstream
当程序需要读写文件时可以使用类std::fstream,该类可以用同一个对象处理这两个任务。示例程序:
#include <fstream>
int main(){
std::fstream tfile("test3.dat",std::ios::in|std::ios::out);
tfile<<"This is TEST3";
tfile.seekg(0);
char tdata[100];
int i=0;
while(!tfile.eof()&&i<sizeof(tdata))
tfile.get(tdata[i++]);
std::cout<<tdata;
return 0;
}