文件流是以外存文件为输入输出对象的数据流。每一个文件流都有一个内存缓冲区与之对应。要以磁盘文件为对象进行输入输出,必须定义一个文件流类的对象,它使数据从内存输出到磁盘文件,或者通过文件流对象将数据从磁盘文件输入到内存。
在C++的 I/O 类库中定义了三种流可以处理文件操作:
- ifstream类,是从输入流 istream 类派生的,支持从磁盘文件的输入操作。
- ofstream类,是从输出流 ostream 类派生的,支持向磁盘文件的输出操作。
- fstream类,是从输入输出流 iostream 类派生的,支持对磁盘文件的输入输出操作。
流对象的定义:
ifstream fInput;
ofstream fOutput;
fstream sIO;
打开文件
- 使用 open() 函数
ifstream fTemps;
fTemps.open("Temp.dat");
- 定义对象时指定参数(常使用这种方式打开文件,简单便捷)
ifstream fGrade("Grade.dat",ios::out); //以输出的方式打开文件Grade.dat
设置文件打开方式
为了对文件操作的方便,我们需要采用不同的打开方式对文件进行操作。同时我们还可以使用或 ‘|’ 对打开方式进行组合。
标识常量 | 意义 |
---|---|
ios::in(输入默认) | 读(输入)方式打开文件 |
ios::out(输出默认1) | 写(输出)方式打开文件 |
ios::ate | 打开文件时,指针指向文件尾 |
ios::app | 追加方式 |
ios::trunc(输出默认2) | 删除文件现有内容 |
ios::nocreate | 如果文件不存在,则打开操作失败 |
ios::noreplace | 如果文件存在,则打开操作失败 |
ios::binary | 二进制方式打开,默认为文本方式 |
同样,打开方式可以在 open() 函数中设置,也可以在流对象中设置。
ifstream infile; //建立输出文件流对象
infile.open("datafile.dat",ios::in); //连接文件,指定以输入方式打开文件
ofstream outfile; //建立输出文件流对象
outfile.open("newfile.dat",ios::out); //连接文件,指定以输出方式打开文件
ifstream infile("datafile.dat",ios::in);
ofstream outfile("newfile.dat",ios::out);
fstream rwfile("myfile.dat",ios::in | ios::out); //连接文件,指定读写方式
关闭文件
文件关闭操作先把缓冲区中的数据完整的写入文件,而后添加文件结束标志,最终切断流对象和外部文件的连接。文件可以调用 close() 函数来关闭。
ofstream ofile; //创建输出文件流
ofile.open("myfile1"); //流与文件关联
ofile.close(); //关闭文件
打开与关闭错误
- 可以使用取反操作符测试文件是否成功打开:
if(!outfile)
{
cout<<"error:unable to open myfile!";
return 1;
}
- 如果关闭未打开的文件,会造成操作失败。为了测试是否成功关闭文件,可以调用 fail() 函数判断:
outfile.close();
if(outfile.fail())
cerr<<"error to close myfiles!";
文本文件的读写
如果文件的每一个字节均以ASCII 代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件,也称文本文件或称字符文件。此类文件用默认打开方式,程序可以从文本文件中读入若干个字符,也可以向他输入一些字符。对这些文本文件进行读写操作,可以使用流运算符 << 和 >> ,也可以调用文件流的 put(),get(),getline() 等成员函数。
(1)将数据输出到文件例程如下,实际是使用 outFile 代替 cout 即可。
// primeNumberstoFile.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <fstream>
#include <iomanip>
int _tmain(int argc, _TCHAR* argv[])
{
const int max = 25;
long primes[max] = {2,3,5}; //素数数组,前3个元素为2,3,5
int count = 3; //记录素数的个数
long trial = 5; //被筛选数
bool isprime = true; //判断素数的bool值
do
{
trial += 2; //更新被筛选数
int i = 0; //素数数组下标
//判别素数
do
{
isprime = trial % *(primes + i) > 0;
} while(++i < count && isprime);
if(isprime )
*(primes + count++) = trial; //将筛选的素数存储到数组中
}while(count<max);
std::ofstream outFile("D:\\ApplicationWorkSpace\\C++\\testStreamState\\primes.txt"); //定义输出流对象,与文件相连
//将所有素数格式化的写入文件
for( int i = 0; i < max; i++ )
{
if(i%5 == 0)
outFile<<std::endl;
outFile<<std::setw(10)<<*(primes + i); //将数组中的数据逐个写入文件
}
return 0;
}
(2)读取文件在屏幕中显示:
// filetoPrimeNumbers.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <iomanip>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
const char* filename = "D:\\ApplicationWorkSpace\\C++\\testStreamState\\primes.txt"; //文件名及路径
ifstream inFile(filename); //创建流对象
//流状态检测
if(!inFile)
{
cout<<endl<<"Failed to open file "<<filename;
return 1;
}
long aprime = 0;
int count = 0; //记录素数的个数
while(!inFile.eof()) //读取数据直到EOF
{
inFile>>aprime; //从文件中读取数据
cout<<(count++ % 5 == 0 ? "\n" : "")//将数据格式化输出到屏幕
<<setw(10)<<aprime;
}
cout<<endl;
return 0;
}
结果
2 3 5 7 11
13 17 19 23 29
31 37 41 43 47
53 59 61 67 71
73 79 83 89 97
请按任意键继续. . .
二进制文件操作
二进制文件将内存中数据不加转换的传送到磁盘文件,并且将数据按二进制的格式存储在各个字节中,因此它又称为内存数据的映像文件或字节文件。
对二进制文件在打开时要用ios::binary指定为以二进制形式传送和存储。对于文件的操作可以使用流类的成员函数 read() 和 write() ,高效便利地读写成批地数据。
(1)读文件函数
原型:istream& read(char *buffer, int len);
使用:
ifstream input("area.dat",ios::binary|ios::in); //打开文件
int iAry[3]; //建立缓冲区
input.read((char*)iAry, 3*sizeof(int));
//read函数常用来读取文件中结构化的记录,对应缓冲区的长度由该结构存储的各种类型的数据决定。
//使用函数成块的传输一个记录,对于不同类型的数据不必进行格式化。
bool readStudent(Stu& oneStudent, ifstream& fStudent)
{
fStudent.read((char*)&oneStudent, sizeof(Stu));
bool ioFlag = fStudent.good();
return ioFlag;
}
(2)写文件函数
原型:ostream& write(const char * buffer, int len);
使用:
Oput.write((char*)iAry, 3*sizeof(int)); //调用方式
//定义写入结构记录的函数
void write(Stu& aStudent, ofstream& fsStuOut)
{
fsStuOut.write((char*)aStudent, sizeof(Stu)); //将一条记录写入文件
if(!fsStuOut.good())
{
cerr<<"\a Error in writing student file."<<endl;
exit(100);
}
return;
}
(3)文件定位函数
二进制文件的读写方式由程序控制,这种类型的文件不是顺序存储的,可以根据文件指针随机读写数据。为了确定文件指针的位 置(指定为long int类型),ios类中定义的设定枚举常量作为参照位置:enum ios::seek_dir{beg = 0, cur = 1, end = 2 }; 文件的开头用 ios::beg 表示,这是文件指针的默认值。指针当前位置用 ios::cur 表示。文件末尾用 ios::end 表示。
文件流中提供了一些有关文件指针的成员函数,使它按用户的意图移动到指定的位置,以便再该位置上进行读写。
- tellp()与tellg():获取指针位置。函数返回当前指针相对于文件起点的偏移量。文本文件中,可以使用tell函数查找文件中数据的位置。tell函数返回0,表明文件指针位于 ios::beg。指针位于第二字节处,则返回1,表明存在一个偏移量。
istream & istream::tellg(); //用于输入文件,返回读指针当所指位置值
ostream & ostream::tellp(); //用于输出文件,返回读指针当所指位置值
- seekp()与seekg():设置指针位置。用于对磁盘文件的定位操作,它们可以设定当前指针在文件中的字节位置,实现定位的功能。seekg()用于定位输入文件的位置,seekp()用于定位输出文件的位置。声明和调用如下:
istream & istream::seekg(long pos); //读指针从流的起始位置向后移动pos个字节
istream & istream::seekg(long off, ios::seek_dir where);
//读指针从流的where位置移动off个字节
ostream & ostream::seekp(long pos); //写指针从流的起始位置向后移动pos个字节
ostream & ostream::seekp(long off, ios::seek_dir where);
//写指针从流的where位置移动off个字节
//调用形式
fsInput.seekg(256L,ios::beg); //等效于fsInput.seekh(256L);
fsOutput.seekp(sizeof(Stu),uos::cur); //指针从当前位置向后移动一条Stu记录的字节数
File.seekp(-40, ios::end); //文件指针从文件尾向前移动40个字节
例子:文件的读写
// fileReadWrite.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <fstream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
fstream f("D:\\ApplicationWorkSpace\\C++\\testStreamState\\DATA.dat",ios::in|ios::out|ios::binary);
//定义一个可输入输出的二进制文件对象
int i;
for( i = 0; i < 20; i++ ) //先写入20个整数
f.write((char*)&i,sizeof(int));
long pos = f.tellp(); //记录当前指针所在位置值
for( i = 20; i < 40; i++ ) //再写入20个整数
f.write((char*)&i,sizeof(int));
f.seekg(pos); //将指针移到pos所表示的位置
f.read((char*)&i,sizeof(int)); //读入一个数据
cout<<"The data stored is "<<i<<endl;
f.seekp(0,ios::beg); //指针移到文件开始
for( i = 0; i < 40; i++ ) //显示全部数据
{
f.read((char*)&i,sizeof(int));
cout<<i<<ends; //ends结束时加一个空格
}
cout<<endl;
return 0;
}
输出结果
The data stored is 40
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
请按任意键继续. . .
字符串流
字符串流:istringstream、ostringstream、stringstream,串流类是ios的派生类。
构造函数原型:
ostringstream::ostringstream(char *buffer, int n, int mode=ios::out);//建立输出字符串流对象
istringstream::istringstream(char *buffer, int n); //建立输入字符串流对象
stringstream::stringstream(char *buffer, int n, int mode); //建立输入输出字符串流对象
采用此方法在建立流对象时,通过给定参数来确定字符串流与特定字符数组的连接。
char buf[80];
ostrstream Output( buf, sizeof(buf) );
由于该字符数组没有相应的结束标志,用户要指定特殊字符作为结束符号。
Output<<x<<"*"<<y<<"="<<x*y<<ends; //插入ends等效于在流中加入'\0'
字符串操作的例子1:
// stringReadWrite.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
string testStr("Input test 256 * 0.5");
string s1,s2,s3;
double x,y;
istringstream input( testStr ); //串流对象与string对象关联
//将字符转换为二进制格式赋给相应变量或对象
input>>s1>>s2>>x>>s3>>y;
cout<<s1<<ends<<s2<<ends
<<x<<s3<<y<<"="<<x*y<<endl;
return 0;
}
结果
Input test 256*0.5=128
请按任意键继续. . .
字符串操作的例子2:
#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
ostringstream Output;
double x,y;
cout<<"Input x and y:";
cin >> x >> y;
//将数据写入流中
Output<<x<<"*"<<y<<"="<<x*y<<endl;
cout<<Output.str(); //显示字符串
return 0;
}
结果
Input x and y:3 4.5
3*4.5=13.5
请按任意键继续. . .