文件指针、文件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; }