文件指针、文件I/O的错误处理、成员函数来处理文件I/O

一、文件指针

先来认识两个指针:

获取指针(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;
}
posted @ 2011-11-26 19:42  csqlwy  阅读(1221)  评论(1编辑  收藏  举报