一、文件指针
先来认识两个指针:
获取指针(get pointer):函数seekg设置获取指针(即移动到要读取的流中的位置),函数tellg检测获取指针(即返回要读取的流中的位置)。
置入指针(put pointer):函数seekp设置置入指针(即移动到要写入的流中的位置),函数tellp检测置入指针(即返回要写入的流中的位置)。
注:seekg和seekp两个参数的版本中:第一个参数表示相对于文件特定位置的偏移;第二个参数指定开始计算偏移的位置,有三个可能值:beg表示文件的开始,cur表示当前指针位置,end表示文件结尾。
MSDN例子:
// basic_ostream_seekp.cpp
// compile with: /EHsc
#include <fstream>
#include <iostream>
int main()
{
using namespace std;
ofstream x("basic_ostream_seekp.txt");
streamoff i = x.tellp();
cout << i << endl;
x << "testing";
i = x.tellp();
cout << i << endl;
x.seekp(2); // Put char in third char position in file
x << " ";
x.seekp(2, ios::end); // Put char two after end of file
x << "z";
}
但是这个文件为什么打开是乱码,而且也不能正常输出 z 呢?
二、文件I/O的错误处理
首先看一个粗糙的处理错误的方法:不管发生什么错误,用同样的方法检测并且执行同样的操作。
#include <iostream>
#include <fstream>
using namespace std;
#include <process.h>
const int MAX=1000;
int buff[MAX];
int main()
{
for (int j=0;j<MAX;j++)
{
buff[j]=j;
}
ofstream os;
os.open("D:edata.bat",ios::trunc|ios::binary);
if (!os)
{
cerr<<"could not open output file\n";
exit(1);
}
cout<<"writing...\n";
os.write(reinterpret_cast<char*>(buff),MAX*sizeof(int));
if (!os)
{
cerr<<"could not write to file\n";
exit(1);
}
os.close();
for (int j=0;j<MAX;j++)
{
buff[j]=0;
}
ifstream is;
is.open("d:edata.bat",ios::binary);
if (!is)
{
cerr<<"could not open intput file\n";
exit(1);
}
cout<<"reading...\n";
is.read(reinterpret_cast<char*>(buff),MAX*sizeof(int));
if (!is)
{
cerr<<"could not read from file\n";
exit(1);
}
for (int j=0;j<MAX;j++)
{
if (buff[j]!=j)
{
cerr<<"\ndata is incorrect\n";
exit(1);
}
}
cout<<"data is correct\n";
return 0;
}
然而我们也可以通过ios的错误状态标志来处理特定的错误。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream file;
file.open("d:text.dat");
if (!file)
{
cout<<"can't open "<<endl;
}
else
cout<<"file opened successfully.";
cout<<"\nfile= "<<file<<endl
<<"error state= "<<file.rdstate()<<endl
<<"good()= "<<file.good()<<endl
<<"eof()= "<<file.eof()<<endl
<<"fail()= "<<file.fail()<<endl
<<"bad()= "<<file.bad()<<endl;
file.close();
return 0;
}
三、使用成员函数的文件I/O
前面都是让main函数来处理文件I/O的细节,当使用复杂的类是,自然就把问价IO操作放到类的成员函数中了。这里简单介绍两个这样的程序。
第一个程序:在每个对象中使用普通的成员函数,用于将自己写入文件或从文件中读出。
第二个程序:演示一个静态成员函数,一次就可以把类的所有对象写入文件或者从文件中读出。
首先,第一个实例:
#include <iostream>
#include <fstream>
using namespace std;
class person
{
protected:
char name[40];
int age;
public:
void getData()
{
cout<<"Enter last name: "; cin>>name;
cout<<"Enter age: "; cin>>age;
}
void showData()
{
cout<<"Name: "<<name<<endl;
cout<<"Age: "<<age<<endl;
}
void diskIn(int);
void diskOut();
static int diskCount();
};
void person::diskIn(int pn)
{
ifstream infile;
infile.open("persfile.dat",ios::binary);
infile.seekg(pn*sizeof(person));
infile.read((char*)this,sizeof(*this));
}
void person::diskOut()
{
ofstream outfile;
outfile.open("persfile.dat",ios::app|ios::binary);
outfile.write((char*)this,sizeof(*this));
}
int person::diskCount()
{
ifstream infile;
infile.open("persfile.dat",ios::binary);
infile.seekg(0,ios::end);
return (int)infile.tellg()/sizeof(person); //返回文件中有多少个person类
}
//
int main()
{
person p;
char ch;
do
{
cout<<"Enter data for person:"<<endl;
p.getData();
p.diskOut(); //写入文件
cout<<"Do another? ";
cin>>ch;
} while (ch=='y');
int n=person::diskCount();
cout<<"There are "<<n<<" persons int file"<<endl;
for (int j=0;j<n;j++)
{
cout<<"person #"<<j<<endl;
p.diskIn(j); //读出文件
p.showData();
}
cout<<endl;
return 0;
}
这里需要强调的是:
① 磁盘操作的所有细节都隐藏到类中了,对主函数是不可见的。
② 因为每个对象都存于内存的不同地方,所以预先并不知道读写的位置。因此,在成员函数中要用到this指针。这里在流的函数read和write中,对象的内存地址经常使用*this来读写,并用sizeo(*this)来读取其大小。
接下来,第二个程序对第一个程序改进。
由于对每个对象都使用成员函数来打开文件、写入对象并关闭文件,显然这种做法的效率不高。如果只打开一次文件,把所有对象写入后再关闭文件,就会更快。------一次将多个对象写入到文件的一个方法,就是使用静态成员函数来应用于整个类而不是单独的对象。
这里我们为了更加贴近实际,我们通过建立雇员employee类,并且建立3个派生类,为了指向派生类,我们当然采用虚函数:通过建一个指针数组指向类型employee的对象。所以 array[j]—>putdata( )就指向派生的三个子类的对象。
但这里我们考虑下,是否可以同样使用sizeof( )来返回指针指向的对象的大小呢??如:outfile.write((char*)arrap[j],sizeof(*array[j]));------上面的写法是不对的,因为函数sizeof不是虚函数,它总是返回基类对象的大小。(这里我们要用到typeid来获得对象的大小)
具体程序:
/************************************************************************/
/* employee.h
/************************************************************************/
#include <iostream>
#include <fstream>
using namespace std;
const int LEN=20;
const int MAX=100;
enum employee_type{tmanager,tscientist,tlaborer};
//
class employee
{
public:
virtual void getdata();
virtual void putdata();
virtual employee_type get_type();
static void add(); //添加雇员
static void display(); //显示多有雇员
static void read(); //读取文件
static void write(); //写入文件
protected:
char name[LEN]; //名字
unsigned long number; //编号
static int n; //雇员当前数量
static employee* arrap[]; //雇员的指针数组
};
//manager
class manager:public employee
{
public:
void getdata();
void putdata();
private:
char title[LEN]; //俱乐部名称
double dues; //会费
};
//scientist
class scientist:public employee
{
public:
void getdata();
void putdata();
private:
int pubs; //发表的文章
};
//laborer
class laborer:public employee
{
};
/************************************************************************/
/* employee.cpp
/************************************************************************/
#include "employee.h"
//静态数据
int employee::n;
employee* employee::arrap[MAX];
//静态函数
void employee::add()
{
char ch;
cout<<"'m' 添加manager"
<<"\n's' 添加scientist"
<<"\n'l' 添加laborer"
<<"\nEnter selection: ";
cin>>ch;
switch (ch)
{
case 'm':arrap[n]=new manager; break;
case 's':arrap[n]=new scientist; break;
case 'l':arrap[n]=new laborer; break;
default:cout<<"Unknow employee type"<<endl; break;
}
arrap[n++]->getdata();
}
void employee::display()
{
for (int j=0;j<n;j++)
{
cout<<(j+1);
switch (arrap[j]->get_type())
{
case tmanager: cout<<". Type:Manager"; break;
case tscientist: cout<<". Type:Scientist";break;
case tlaborer: cout<<". Type:Laborer"; break;
default: cout<<". Unknow Typ:";
}
cout<<endl;
arrap[j]->putdata();
//cout<<endl;
}
}
employee_type employee::get_type()
{
if (typeid(*this)==typeid(manager))
return tmanager;
else if (typeid(*this)==typeid(scientist))
return tscientist;
else if (typeid(*this)==typeid(laborer))
return tlaborer;
else
{
cerr<<"\nBad employee type";
exit (1);
}
return tmanager;
}
void employee::write()
{
int size;
cout<<"Writing "<<n<<" employee."<<endl;
ofstream outfile;
employee_type etype;
outfile.open("EMPLOY.DAT",ios::trunc|ios::binary);
if (!outfile)
{
cout<<"Cant open file"<<endl;
return;
}
for (int j=0;j<n;++j)
{
etype=arrap[j]->get_type();
outfile.write((char*)&etype,sizeof(etype)); //首先写入对象的类型
switch(etype) //通过类型确定对象的大小
{
case tmanager: size=sizeof(manager); break;
case tscientist: size=sizeof(scientist); break;
case tlaborer: size=sizeof(laborer); break;
}
outfile.write((char*)(arrap[j]),size); //其次写入对象的内容
if (!outfile)
{
cout<<"Cant write to file! "<<endl;
}
}
}
void employee::read() //读出文件
{
int size;
employee_type etype;
ifstream infile;
infile.open("EMPLOY.DAT",ios::binary);
n=0;
while (true)
{
infile.read((char*)&etype,sizeof(etype)); //首先读取文件中对象的类型
if (infile.eof())
break;
if (!infile)
{
cerr<<"Cant read type from file."<<endl;
return;
}
switch(etype) //确定对象的位置和大小
{
case tmanager:
arrap[n]=new manager;
size=sizeof(manager);
break;
case tscientist:
arrap[n]=new scientist;
size=sizeof(scientist);
break;
case tlaborer:
arrap[n]=new laborer;
size=sizeof(laborer);
break;
default: cout<<"Unknow type in file."<<endl;return;
}
infile.read((char*)(arrap[n]),size); //从文件中读取对象
if (!infile)
{
cerr<<"Cant read data from file."<<endl;
return ;
}
n++;
}
cout<<"reading "<<n<<" employees"<<endl;
}
//employee
void employee::getdata()
{
cout<<"Enter the name: "; cin>>name;
cout<<"Enter the number: "; cin>>number;
}
void employee::putdata()
{
cout<<"The name: "<<name<<endl;
cout<<"The number: "<<number<<endl;
}
//manager
void manager::getdata()
{
employee::getdata();
cout<<"Enter the title: "; cin>>title;
cout<<"Enter the dues: "; cin>>dues;
}
void manager::putdata()
{
employee::putdata();
cout<<"The title: "<<title<<endl;
cout<<"The dues: "<<dues<<endl;
}
//scientist
void scientist::getdata()
{
employee::getdata();
cout<<"Enter the pubs: "; cin>>pubs;
}
void scientist::putdata()
{
employee::putdata();
cout<<"The pubs: "<<pubs<<endl;
}
/************************************************************************/
/* main.cpp
/************************************************************************/
#include "employee.h"
int main()
{
char ch;
while (true)
{
cout<<"a--添加雇员"<<endl
<<"d--显示全部雇员"<<endl
<<"w--把所有的雇员写入文件"<<endl
<<"r--读出所有的雇员"<<endl
<<"x--退出"<<endl
<<"选择类型:";
cin>>ch;
switch(ch)
{
case 'a':
employee::add(); break;
case 'd':
employee::display(); break;
case 'w':
employee::write(); break;
case 'r':
employee::read(); break;
case 'x':
exit(0);
default: cout<<"Unknow command";
}
}
return 0;
}