随机访问文件
任务:写一个通用程序,为任何类型的记录生成随机访问文件。该程序运行后生成了一个包含个人记录的文件,每条记录由5个数据成员(社会安全号码、姓名、所在城市、出生年份以及薪水)组成,该程序还生成了一个存储学生记录的学生文件,学生记录的数据成员与个人纪录相同,另外还增加了学院专业,再次使用了继承。
此案例分析中,通用的随机访问文件程序能够将新记录插入文件,在文件中查找记录,还可以修改记录。文件名由用户提供,如果没有找到该文件,就新建一个;否则,打开文件进行读写。
#pragma once
#ifndef PERSONAL
#define PERSONAL
#include<fstream> //用fstream输入输出类来读写文件
#include<cstring>
using namespace std;
class Personal //个人记录类
{
public:
Personal();
Personal(char*, char*, char*, int, long);
void writeToFile(fstream&) const;
void readFromFile(fstream&);
void readKey();
int size() const {
return 9 + nameLen + cityLen + sizeof(year) + sizeof(salary);
}
bool operator==(const Personal& pr) const { //重载==运算符,用于后面判断两个成员是否为同一人
return strncmp(pr.SSN, SSN, 9) == 0; //这里的判断只需判断SSN即可,因为每个人的SSN都是独一无二的
}
protected:
const int nameLen, cityLen;
char SSN[10], *name, *city;
int year;
long salary;
ostream& writeLegibly(ostream&);
friend ostream& operator<<(ostream& out, Personal& pr) {
return pr.writeLegibly(out);
}
istream& readFromComsole(istream&);
friend istream& operator>>(istream& in, Personal& pr) {
return pr.readFromComsole(in);
}
};
#endif // !PERSONAL
下面是person类函数的具体实现
#include "Personal.h"
#include<iostream>
Personal::Personal() :nameLen(10), cityLen(10) {
name = new char[nameLen + 1]; //别忘记字符串是以标志位'\0'结束的
city = new char[cityLen + 1];
}
Personal::Personal(char *ssn, char *n, char *c, int y, long s) :
nameLen(10), cityLen(10) {
name = new char[nameLen + 1];
city = new char[cityLen + 1];
strcpy(SSN, ssn);
strcpy(name, n);
strcpy(city, c);
year = y;
salary = s;
}
void Personal::writeToFile(fstream& out) const {
out.write(SSN, 9);
out.write(name, nameLen);
out.write(city, cityLen);
out.write(reinterpret_cast<const char*>(&year), sizeof(int)); //这里需要把整型变量转成常量指针
out.write(reinterpret_cast<const char*>(&salary), sizeof(int));
}
void Personal::readFromFile(fstream& in) {
in.read(SSN, 9);
in.read(name, nameLen);
in.read(city, cityLen);
in.read(reinterpret_cast<char*>(&year), sizeof(int));
in.read(reinterpret_cast<char*>(&salary), sizeof(int));
}
void Personal::readKey() {
char s[80];
cout << "Enter SSN: ";
cin.getline(s, 80);
strncpy(SSN, s, 9);
}
ostream& Personal::writeLegibly(ostream& out) { //重载输出运算符
SSN[9] = name[nameLen] = city[cityLen] = '\0';
out << "SSN=" << SSN << ",name=" << name
<< ",city=" << city << ",year=" << year
<< ",salary=" << salary;
return out;
}
istream& Personal::readFromComsole(istream& in) { //重载输入运算符
SSN[9] = name[nameLen] = city[cityLen] = '\0';
char s[80];
cout << "SSN: ";
in.getline(s, 80);
strncpy(SSN, s, 9);
cout << "Name: ";
in.getline(s, 80);
strncpy(name, s, nameLen);
cout << "City: ";
in.getline(s, 80);
strncpy(city, s, cityLen);
cout << "Birthyear: ";
in >> year;
cout << "Salary: ";
in >> salary;
in.ignore();
return in;
}
接下来是学生类的实现,从要求中我们可以知道学生类是person类的派生,所以student类只需要继承person类,增加额外的数据成员并重载某些类函数即可。
#ifndef STUDENT
#define STUDENT
#include "personal.h"
class Student : public Personal {
public:
Student();
Student(char*,char*,char*,int,long,char*);
void writeToFile(fstream&) const;
void readFromFile(fstream&);
int size() const {
return Personal::size() + majorLen;
}
protected:
char *major;
const int majorLen;
ostream& writeLegibly(ostream&);
friend ostream& operator<<(ostream& out, Student& sr) {
return sr.writeLegibly(out);
}
istream& readFromConsole(istream&);
friend istream& operator>>(istream& in, Student& sr) {
return sr.readFromConsole(in);
}
};
#endif
接下来是student某些类函数的重写
#include "student.h"
#pragma warning(disable : 4996)
Student::Student() : majorLen(10) {
Personal();
major = new char[majorLen+1];
}
Student::Student(char *ssn, char *n, char *c, int y, long s, char *m) :
majorLen(11) {
Personal(ssn,n,c,y,s);
major = new char[majorLen+1];
strcpy(major,m);
}
void Student::writeToFile(fstream& out) const {
Personal::writeToFile(out);
out.write(major,majorLen);
}
void Student::readFromFile(fstream& in) {
Personal::readFromFile(in);
in.read(major,majorLen);
}
ostream& Student::writeLegibly(ostream& out) {
Personal::writeLegibly(out);
major[majorLen] = '\0';
out << ", major = " << major;
return out;
}
istream& Student::readFromConsole(istrieam& in) {
Personal::readFromConsole(in);
char s[80];
cout << "Major: ";
in.getline(s,80);
strncpy(major,s,9);
return in;
}
有了两个需要记录的类,接下来咱们需要使用一个Database去记录person类或者student类,并实现要求的随机访问文件
#ifndef DATABASE
#define DATABASE
template<class T>
class Database {
public:
Database();
void run();
private:
fstream database;
char fName[20];
ostream& print(ostream&); //输出文件记录内容,方便用户查看
void add(T&); //在文件尾部追加记录
bool find(const T&); //确定记录是否在文件中。
void modify(const T&); //更新存储在特定记录中的信息。
friend ostream& operator<<(ostream& out, Database& db) {
return db.print(out);
}
};
#endif
Database类函数具体形式以及程序运行测试
#include "personal.h"
#include "student.h"
#include "database.h"
template<class T>
Database<T>::Database() {
}
template<class T>
void Database<T>::add(T& d) {
database.open(fName,ios::in|ios::out|ios::binary);
database.clear();
database.seekp(0,ios::end);
d.writeToFile(database);
database.close();
}
template<class T>
void Database<T>::modify(const T& d) {
T tmp;
database.open(fName,ios::in|ios::out|ios::binary);
database.clear();
while (!database.eof()) {
tmp.readFromFile(database);
if (tmp == d) { // overloaded ==
cin >> tmp; // overloaded >>
database.seekp(-d.size(),ios::cur);
tmp.writeToFile(database);
database.close();
return;
}
}
database.close();
cout << "The record to be modified is not in the database\n";
}
template<class T>
bool Database<T>::find(const T& d) {
T tmp;
database.open(fName,ios::in|ios::binary);
database.clear();
while (!database.eof()) {
tmp.readFromFile(database);
if (tmp == d) { // overloaded ==
database.close();
return true;
}
}
database.close();
return false;
}
template<class T>
ostream& Database<T>::print(ostream& out) {
T tmp;
database.open(fName,ios::in|ios::binary);
database.clear();
while (true) {
tmp.readFromFile(database);
if (database.eof())
break;
out << tmp << endl; // overloaded <<
}
database.close();
return out;
}
template<class T>
void Database<T>::run() {
cout << "File name: ";
cin >> fName;
cin.ignore(); // skip '\n';
database.open(fName,ios::in);
if (database.fail())
database.open(fName,ios::out);
database.close();
char option[5];
T rec;
cout << "1. Add 2. Find 3. Modify a record; 4. Exit\n";
cout << "Enter an option: ";
while (cin.getline(option,5)) {
if (*option == '1') {
cin >> rec; // overloaded >>
add(rec);
}
else if (*option == '2') {
rec.readKey();
cout << "The record is ";
if (find(rec) == false)
cout << "not ";
cout << "in the database\n";
}
else if (*option == '3') {
rec.readKey();
modify(rec);
}
else if (*option != '4')
cout << "Wrong option\n";
else return;
cout << *this; // overloaded <<
cout << "Enter an option: ";
}
}
int main() {
Database<Personal>().run();
// Database<Student>().run();
return 0;
}