实验目的

构建一个图书馆系统,包含

1.类$BookRecord$,对单本数记录,记录内容为书籍编号,书籍名,书籍作者,出版年份,可借阅量,已借出量

2.类$Borrower$,对借阅者进行登记,登记内容为姓名,身份id,借书量,书籍编号信息

3.类$Catalogue$,访问上述两个类,即实现对已存书的访问和对借书人的访问

4.类$Library$,作为整个系统的载体,后续添加额外功能需要从此入手

实验过程

以实验目的的顺序完成图书馆的构建

$BookRecord$

BookRecord::BookRecord()
{
book_ava_num=0;
book_loan=0;
};

初始化,将存书和借书数目默认为0

BookRecord::BookRecord(string i, string t, string a, string y, int n, int l) : book_id(i), book_title(t),
book_auther(a), book_year(y), book_ava_num(n), book_loan(l)
{
if (book_id[0] < 65 || book_id[0] > 90)
{
cout << "书籍ID错误" << endl;
exit(0);
}
if (book_year[0] < 49 || book_id[0] > 50)
{
cout << "出版时间错误" << endl;
exit(0);
}
if (book_id.length() != 4)
{
cout << "书籍ID错误" << endl;
exit(0);
}
};// 检查ID首字母大写,year首字是否为1,2,是否有4位。65-90

含参构造,为单个书提供直接输入的构造函数,同时完成对ID,year的判断

BookRecord::~BookRecord(){};

析构函数

void BookRecord::write(ifstream &ifs) // 写入数据
{
getline(ifs,book_id,';');
cout<<book_id;
cout<<book_id.length()<<endl;
if (book_id[1] < 65 || book_id[1] > 90)
{
cout << "书籍FID错误" << endl;
exit(0);
}
if (book_id.length() != 5)
{
cout << "书籍LID错误" << endl;
exit(0);
}
getline(ifs, book_title,';');
getline(ifs, book_auther,';');
getline(ifs,book_year,';');
if (book_year[0] < 49 || book_id[0] > 50)
{
cout << "出版日期错错误" << endl;
exit(0);
}
ifs >> book_ava_num;
ifs >> book_loan;
}

写入数据,在后续创建类$BookRecord$的数组时,需要此函数通过ifstream获取文件中的数据

此外,在输入完成后,对ID,year进行判断是否合法,如不合法,使用exit(0)退出

string BookRecord::id()
{
return book_id;
}
string BookRecord::return_ID()
{
return book_id;
}

返回private的成员值

void BookRecord::write_booknum()
{
book_ava_num--;
if (book_ava_num < 0)
{
cout << "书库殆尽,无法借阅" << endl;
book_ava_num = 0;
}
}
void BookRecord::write_bookloan()
{
book_loan++;
}

为单本图书的借阅提供接口,并判断合法性

void BookRecord::check() // 返回各项数据
{
cout << "Book ID :" << book_id << endl;
cout << "Title :" << book_title << endl;
cout << "Author :" << book_auther << endl;
cout << "Year published :" << book_year << endl;
cout << "Total number of copies :" << book_ava_num << endl;
cout << "Number available for loan :" << book_loan << endl;
} // 记书类的各项数据,加入目录

返回类中记录的所有数据,用于检查书籍完全度

$Borrower$

Borrower::Borrower()
{
loan=0;
loan_books=NULL;
};

默认构造,将动态数组设置为空,等待后续赋值

Borrower::Borrower(string i, string b, int l) : borrower_id(i), borrower(b), loan(l)
{
if (borrower_id.length() != 5)
{
cout << "借阅人ID错误" << endl;
exit(0);
}
};

为单位人的借阅提供接口

void Borrower::write(ifstream& ifs)
{
getline(ifs, borrower_id,';');
cout<<borrower_id.length();
if (borrower_id.length() != 6)
{
cout << "用户ID错误" << endl;
exit(0);
}
getline(ifs, borrower,';');
ifs >> loan;
createbook(loan);
readbook(ifs);
} // 写入数据

传入ifstream&,输入对应的数据并创建动态数组,转至readbook中继续读取

void Borrower::readbook(ifstream& ifs)
{
for (int i = 0; i < loan; i++)
{
getline(ifs,loan_books[i],';');
cout<<loan_books[i];
cout<<loan_books[i].length()<<endl;
if (loan_books[i][1] < 65 || loan_books[i][1] > 90)
{
cout << "书籍ID错误" << endl;
exit(0);
}
}
}

传入ifstream&,从文件中读取借阅人的书籍数据,同时对书籍的编号判断合法性

void Borrower::createbook(int a)//开动态数组
{
loan=a;
loan_books=new string[loan];
}

创建动态数组的函数

string Borrower::bookid(int i)
{
if(i>loan)
exit(0);
else
return loan_books[i];
}
int Borrower::loan_bor()
{
return loan;
}
void Borrower::return_id()
{
for (int i = 0; i < loan; i++)
{
cout << loan_books[i] << " ";
}
cout << endl;
}

返回借书的编号,用于和BookRecord比对,然后借阅,第一个为单个查看,后两个为依此查看

void Borrower::check()
{
cout << "借阅人ID :" << borrower_id << endl;
cout << "姓名 :" << borrower << endl;
for (int i = 0; i < loan; i++)
{
cout << "第 " << i << " 个 :";
cout << "借出书的数目 :" << loan_books[i] << endl;
}
}

返回借阅人全部信息的函数

Borrower::~Borrower()
{
delete[] loan_books;
};

记得对动态数组进行释放

$Catalogue$

Catalogue::Catalogue()
{
book_total = 0;
book_borrow=0;
bookres = NULL;
bor=NULL;
}

初始构造,将两个动态数组都设置为空

void Catalogue::writebook(int& a)
{
book_total = a;
}
void Catalogue::writeloan(int& a)
{
book_borrow = a;
}

写入书籍书和借阅人数的函数

void Catalogue::create_record(ifstream &ifs)
{
bookres = new BookRecord[book_total];
for (int i = 0; i < book_total; i++)
{
bookres[i].write(ifs);
}
}

创建BookRecord的动态数组,并调用write完成写入数据

void Catalogue::create_borrow(ifstream &ifs)
{
bor = new Borrower[book_borrow];
for (int i=0; i<book_borrow; i++)
{
bor[i].write(ifs);
find_record(bookres, bor[i]); // 传入bookres,用于和bor中的借书名单比对修改
}
}

创建Borrower的动态数组,并调用write完成写入借阅人和借阅数的数据

void Catalogue::find_record(BookRecord a[], Borrower b)
{
int borrow_num = b.loan_bor(); // 借书数目,用于循环
for (int i = 0; i < book_borrow; i++) // 一层循环,借书名单
{
for (int j = 0; j < borrow_num; j++) // 二层循环,记书名单
{
if (a[i].id() == b.bookid(j))
{
a[i].write_bookloan();
a[i].write_booknum(); // 修改记书名单中每个书的留存
}
}
}
}
void Catalogue::find_ID(BookRecord a[], string id)
{
for (int i = 0; i < book_total; i++)
{
if (a[i].return_ID() == id)
a[i].check(); // cheack输出所有信息
}
}

获取借阅书的数目后循环访问BookRecord数组,以完成对可借阅书的数目的操作

BookRecord* Catalogue::re_bookres()
{
return bookres;
}
Borrower* Catalogue::re_borrow()
{
return bor;
}

返回相关数据

Catalogue::~Catalogue()
{
delete bookres;
delete bor;
}

释放内存

$Library$

Library::Library(int a, int b) : books(a), loan(b){};

含参构造,对存书量和借阅量赋值

Library::Library()
{
loan=0;
books=0;
};

默认构造,初始化

void Library::write_book(ifstream &ifs)
{
ifs >> books;
cout<<"记载了"<<books<<"本书"<<endl;
};
void Library::write_loan(ifstream &ifs)
{
ifs >> loan;
cout<<"记载了"<<loan<<"个借阅人"<<endl;
};

根据ifs的输入提示相应的行为

int& Library::getbooks()
{
return books;
}
int& Library::getloan()
{
return loan;
}
void Library::lookbook(int a)
{
cata.re_bookres()[a].check();
} // 打出记载的图书信息
void Library::lookborrow(int a)
{
cata.re_borrow()[a].check();
} // 打出借阅人信息
Catalogue& Library::re_cltalogu()
{
return cata;
}

返回信息函数,第三四函数返回catalougue的两个对象的相关数据

Library::~Library(){};

$ifstream$

该段主要为文件操作部分

$ifstream$

ifstream ifs;
ifs.open("date.txt", ios::in);
if (ifs.is_open())
{
library.write_book(ifs,ofs);// 写入图书总数
library.write_loan(ifs,ofs); // 写入借阅总数
library.re_cltalogu().writebook(library.getbooks()); // 为cata内的图书总数赋值
library.re_cltalogu().writeloan(library.getloan()); // 为cata内的借阅人数赋值
library.re_cltalogu().create_record(ifs);// 写入具体存入数据
library.re_cltalogu().create_borrow(ifs);
for(int i=0;i<library.getbooks();i++)
library.re_cltalogu().re_bookres()[i].check(ofs);
}
ifs.close();//记得关门

创建对象后打开文件$ifs.open(“date.txt”, ios::in)$,接着用$ifs.is_open()$检查是否打开文件。若打开,则以&的方式传入函数进行输入。

在Library.cpp中开输入流对象,能有效缩小开销。
建议写完之后close文件
使用”\n“作为getline的标准会导致问题,经常出现只读一半的问题,改为$getline(ofs,line,’;’)$后就便成为空格读取不当的问题(这问题在读取到换行符时也会出现),需要手动在文件中添加空格,如需对读取的内容进行判断,还要删去空格。

当前样式
N123;Object-Oriented Programming;Dave Smith;2009;3 1
先前样式
N123
Object-Oriented Programming
Dave Smith
2009
3 1
但是,在对不含空格的一行使用getline时会出现跨行读取的问题。故使用ifs>>读取

$ofstream$

ofstream ofs("out.txt");
ifs.open("date.txt", ios::in);
if(!ofs.is_open())
{
cout<<"文件打开失败"<<endl;
return 0;
}
if (ifs.is_open())
{
library.write_book(ifs,ofs);// 写入图书总数
library.write_loan(ifs,ofs); // 写入借阅总数
library.re_cltalogu().writebook(library.getbooks()); // 为cata内的图书总数赋值
library.re_cltalogu().writeloan(library.getloan()); // 为cata内的借阅人数赋值
library.re_cltalogu().create_record(ifs);// 写入具体存入数据
library.re_cltalogu().create_borrow(ifs);
for(int i=0;i<library.getbooks();i++)
library.re_cltalogu().re_bookres()[i].check(ofs);
}
ofs.close();

创建输出流对象,$out.txt$若存在,则打开,否则创建同名文件进行操作,以同样的方法传入函数,输出内容到文件。
读取文件时,建议当前目录下无out.txt文件,这样运行时能直接看到文件的产生
文件内是否有内容也可以成为程序是否能跑通的标准。当出现编译器未指出,且程序有时能跑通,有时不行的问题时,ofs指向的文件不会产出数据。将ofs放在你觉得有问题的地方就行
完了

文件展示

$data.txt$

3
3
N123;Object-Oriented Programming;Dave Smith;2009;3 1
A251;UML Modelling and Design;Barry Arthurs;2005;1 0
Z001;Practical Guide to STL;John Johnson;2000;5 2
12345;Joe Bloggs; 3 N123; A251; Z001;
20498;Alex Alexis;1 Z001;
19090;Mike Mike;2 N123; Z001;

$out.txt$

记载了3本书
记载了3个借阅人
Book ID :
N123
Title :Object-Oriented Programming
Author :Dave Smith
Year published :2009
Total number of copies :3
Number available for loan :0
Book ID :
A251
Title :UML Modelling and Design
Author :Barry Arthurs
Year published :2005
Total number of copies :1
Number available for loan :0
Book ID :
Z001
Title :Practical Guide to STL
Author :John Johnson
Year published :2000
Total number of copies :5
Number available for loan :0

实验代码

$Library.h$

#include<iostream>
#include<string>
#include <fstream>
using namespace std;
#ifndef LIBRARY_H
#define LIBRARY_H
class BookRecord
{
public:
BookRecord();//默认构造函数
BookRecord(string,string,string,string,int,int);//检查ID首字母大写,year首字是否为1,2,是否有4位。65-90
void write(ifstream&);//写入数据
string id();
string return_ID();
void write_booknum();
void write_bookloan();
void check(ofstream&);
void check();//返回各项数据
~BookRecord();
private:
string book_id;
string book_title;
string book_auther;
string book_year;
int book_total_num;
int book_avail;
};//记书类的各项数据,加入目录
class Borrower
{
public:
Borrower();
Borrower(string,string,int);
void readbook(ifstream&);//逐个写入书
void write(ifstream&);//写入数据
string bookid(int);
int loan_bor();//返回借书数
void return_id();//逐次输出借的书
void check();//判断合法
void createbook(int);
~Borrower();
private:
string borrower_id;
string borrower;
int loan;//借书目
string *loan_books=nullptr;//借书数组
};//借书类的各项数据,加入目录
class Catalogue
{
public:
Catalogue();
void writebook(int& );//写入存书量
void writeloan(int& );//写入借阅人
void create_record(ifstream&);
void create_borrow(ifstream&);
void find_record(BookRecord [],Borrower& );
void find_ID(BookRecord [],string);
BookRecord* re_bookres();
Borrower* re_borrow();
~Catalogue();
private:
BookRecord *bookres=nullptr;//存书目
Borrower *bor=nullptr;//借阅人
int book_total;
int book_borrow;
};//目录类中可访问总记书量和借书人的数目。
class Library
{
public:
Library(int,int);
Library();
void write_book(ifstream&,ofstream&);
void write_loan(ifstream&,ofstream&);
int& getbooks();
int& getloan();
void lookbook(int);//打出记载的图书信息
void lookborrow(int);//打出借阅人信息
Catalogue& re_cltalogu();
~Library();
private:
Catalogue cata;//目录维护
int loan;
int books;
};//该类目前只包含数据的返回和读写
#endif

$Library.cpp$

#include <iostream>
#include <string>
#include <fstream>
#include "Library.h"
using namespace std;
BookRecord::BookRecord()
{
book_total_num=0;
book_avail=0;

};
BookRecord::BookRecord(string i, string t, string a, string y, int n, int l) : book_id(i), book_title(t),
book_auther(a), book_year(y), book_total_num(n), book_avail(l)
{
if (book_id[0] < 65 || book_id[0] > 90)
{
cout << "书籍ID错误" << endl;
exit(0);
}
if (book_year[0] < 49 || book_id[0] > 50)
{
cout << "出版时间错误" << endl;
exit(0);
}
if (book_id.length() != 4)
{
cout << "书籍ID错误" << endl;
exit(0);
}
};// 检查ID首字母大写,year首字是否为1,2,是否有4位。65-90

BookRecord::~BookRecord(){};

void BookRecord::write(ifstream &ifs) // 写入数据
{
getline(ifs,book_id,';');
if (book_id[1] < 65 || book_id[1] > 90)
{
cout << "书籍FID错误" << endl;
exit(0);
}
if (book_id.length() != 5)
{
cout << "书籍LID错误" << endl;
exit(0);
}
getline(ifs, book_title,';');
getline(ifs, book_auther,';');
getline(ifs,book_year,';');
if (book_year[0] < 49 || book_id[0] > 50)
{
cout << "出版日期错错误" << endl;
exit(0);
}
ifs >> book_total_num;
ifs >> book_avail;
}
string BookRecord::id()
{
return book_id;
}
string BookRecord::return_ID()
{
return book_id;
}
void BookRecord::write_booknum()
{
book_avail--;
if (book_avail < 0)
{
cout << "书库殆尽,无法借阅" << endl;
book_avail = 0;
}
}
void BookRecord::write_bookloan()
{
book_avail--;
}
void BookRecord::check(ofstream& ofs) // 返回各项数据
{
ofs << "Book ID :" << book_id << endl;
ofs << "Title :" << book_title << endl;
ofs << "Author :" << book_auther << endl;
ofs << "Year published :" << book_year << endl;
ofs << "Total number of copies :" << book_total_num << endl;
ofs << "Number available for loan :" << book_avail << endl;
} // 记书类的各项数据,加入目录
void BookRecord::check() // 返回各项数据
{
cout << "Book ID :" << book_id << endl;
cout << "Title :" << book_title << endl;
cout << "Author :" << book_auther << endl;
cout << "Year published :" << book_year << endl;
cout << "Total number of copies :" << book_total_num << endl;
cout << "Number available for loan :" << book_avail << endl;
} // 记书类的各项数据,加入目录
Borrower::Borrower()
{
loan=0;
loan_books=nullptr;
};
Borrower::Borrower(string i, string b, int l) : borrower_id(i), borrower(b), loan(l)
{
if (borrower_id.length() != 5)
{
cout << "借阅人ID错误" << endl;
exit(0);
}
};
void Borrower::readbook(ifstream& ifs)
{
for (int i = 0; i < loan; i++)
{
getline(ifs,loan_books[i],';');
if (loan_books[i][1] < 65 || loan_books[i][1] > 90)
{
cout << "书籍ID错误" << endl;
exit(0);
}
}
}
void Borrower::write(ifstream& ifs)
{
getline(ifs, borrower_id,';');
if (borrower_id.length() != 6)
{
cout << "用户ID错误" << endl;
exit(0);
}
getline(ifs, borrower,';');
ifs >> loan;
createbook(loan);
readbook(ifs);
} // 写入数据
void Borrower::createbook(int a)//开动态数组
{
loan=a;
this->loan_books=new string[loan];
}
string Borrower::bookid(int i)
{
if(i>loan-1)
{
cout<<i;
exit(0);
}
else
return loan_books[i];
}
int Borrower::loan_bor()
{
return loan;
}
void Borrower::return_id()
{
for (int i = 0; i < loan; i++)
{
cout << loan_books[i] << " ";
}
cout << endl;
}
void Borrower::check()
{
cout << "借阅人ID :" << borrower_id << endl;
cout << "姓名 :" << borrower << endl;
for (int i = 0; i < loan; i++)
{
cout << "第 " << i << " 个 :";
cout << "借出书的数目 :" << loan_books[i] << endl;
}
}
Borrower::~Borrower()
{
if(loan_books!=nullptr)
{
delete [] loan_books;
}
};
Catalogue::Catalogue()
{
book_total = 0;
book_borrow=0;
bookres=nullptr;
bor=nullptr;
}
void Catalogue::writebook(int& a)
{
book_total = a;
}
void Catalogue::writeloan(int& a)
{
book_borrow = a;
}
void Catalogue::create_record(ifstream &ifs)
{
bookres = new BookRecord[book_total];
for (int i = 0; i < book_total; i++)
{
bookres[i].write(ifs);
}
}
void Catalogue::create_borrow(ifstream &ifs)
{
bor = new Borrower[book_borrow];
for (int i=0; i<book_borrow; i++)
{
bor[i].write(ifs);
find_record(bookres, bor[i]); // 传入bookres,用于和bor中的借书名单比对修改
}
}
void Catalogue::find_record(BookRecord *a, Borrower &b)
{
int borrow_num = b.loan_bor(); // 借书数目,用于循环
for (int i = 0; i < book_total; i++) // 一层循环,借书名单
{
for (int j = 0; j < borrow_num; j++) // 二层循环,记书名单
{
string q1=a[i].id();
string q2=b.bookid(j);
q2.erase(0,1);
q1.erase(0,1);
if (q1==q2)
{
a[i].write_booknum(); // 修改记书名单中每个书的留存
}
}
}
}
void Catalogue::find_ID(BookRecord a[], string id)
{
for (int i = 0; i < book_total; i++)
{
if (a[i].return_ID() == id)
a[i].check(); // cheack输出所有信息
}
}
BookRecord* Catalogue::re_bookres()
{
return bookres;
}
Borrower* Catalogue::re_borrow()
{
return bor;
}
Catalogue::~Catalogue()
{
if(bookres!=nullptr)
delete []bookres;
if(bor!=nullptr)
delete []bor;
}
Library::Library(int a, int b) : books(a), loan(b){}
Library::Library()
{
loan=0;
books=0;
};
void Library::write_book(ifstream &ifs,ofstream& ofs)
{
ifs >> books;
ofs<<"记载了"<<books<<"本书"<<endl;
};
void Library::write_loan(ifstream &ifs,ofstream& ofs)
{
ifs >> loan;
ofs<<"记载了"<<loan<<"个借阅人"<<endl;
};
int& Library::getbooks()
{
return books;
}
int& Library::getloan()
{
return loan;
}
void Library::lookbook(int a)
{
cata.re_bookres()[a].check();
} // 打出记载的图书信息
void Library::lookborrow(int a)
{
cata.re_borrow()[a].check();
} // 打出借阅人信息
Catalogue& Library::re_cltalogu()
{
return cata;
}
Library::~Library(){};// 该类目前只包含数据的返回和读写

$mian.cpp$

#include <iostream>
#include <string>
#include <fstream>
#include "Library.h"
#include "Library.cpp"
using namespace std;
int main()
{
Library library;
ifstream ifs;
ofstream ofs("out.txt");
ifs.open("date.txt", ios::in);
if(!ofs.is_open())
{
cout<<"文件打开失败"<<endl;
return 0;
}
if (ifs.is_open())
{
library.write_book(ifs,ofs);// 写入图书总数
library.write_loan(ifs,ofs); // 写入借阅总数
library.re_cltalogu().writebook(library.getbooks()); // 为cata内的图书总数赋值
library.re_cltalogu().writeloan(library.getloan()); // 为cata内的借阅人数赋值
library.re_cltalogu().create_record(ifs);// 写入具体存入数据
library.re_cltalogu().create_borrow(ifs);
for(int i=0;i<library.getbooks();i++)
library.re_cltalogu().re_bookres()[i].check(ofs);
}
ifs.close();
ofs.close();
return 0;
}
// 可以开书籍序号数组,这样可以直接访问和读取,但也就意味着要拆解BookRecorder类

实验心得与改进

  • ifs可以开在Library.cpp中,避免传入传出的繁琐。
  • ofs读取文件时存在问题,用”;“代替\n作为结束判断
  • Library类的内容不足,可以添加其他功能
  • 可以改进Catalogue,将书籍索引改为数组直接访问,可以实现长期运营
  • 动态数组的指针悬挂问题需要细心解决