图书信息管理系统实验报告
一、实验摘要
实现一个图书信息管理系统,实现的功能有采编、查询和借阅、还书与续借、书目删除、以及管理员直接操作数据库的模式。本程序的几处特色是处理输入不符合规范的字符(串)、代码重用、数据即时更新、根据当前窗口状态将主窗口与其它窗口的并行输出、操作的可撤回性和连惯性,以及数据库的保护隐藏。
二、实验平台
机型:ThinkPad E465 | 系统:Win10 | 软件:记事本、cmd | 编译环境:G++
三、实验目的
1、通过构造数据库加深对数据结构的认识,主要是自定义类与结构体的实现使用。
2、加深对数据遍历方式的认识,这里主要用到了线性遍历。
3、掌握线性表的优化,如存储空间上的优化、查找性能上的优化等。
四、实验原理
- 总体来看,本程序采用自顶向下的设计方法,先确定管理系统的大致框架,即一个主窗口加上采编、查询和借阅、还书与续借、书目删除、以及管理员直接操作数据库的模式的窗口,再实现各窗口的布局和功能,最后定义一个Database类以及相关全局变量逐个实现这些功能。
- Database类中包括自定义的书籍结构体数组用于存放书籍名称、作者、出版社、借阅情况等信息,以及辅助变量记录书本书目数量、书本总量、书本剩余量等,类的方法实现了如上所述功能,此外还有一些用于判断状态、选择相应选项的全局变量,在后面解释各个函数的功能时会一一介绍。
- 在数据库的安全方面。本系统的数据库文件采用了NTFS文件流加密,即只能通过本系统访问这个数据库,从系统外部不能轻易打开并修改数据库文件内的重要数据(当然,如果懂得相关技术可以将此文件解密出来进行修改)。把数据库打包成压缩文件时需在“高级”选项中勾选“保存文件流数据”才能保证数据库的完整。这样提高了数据库的安全性。此外,在每个功能结束后即时重写数据库文件,更新数据库,避免系统突发故障中止时丢失数据。
- 根据窗口状态来输出各个窗口。这里将各个功能窗口用各个函数实现(包括主窗口),用一个while(now != m_exit){…}循环无限执行判断窗口状态(now)的命令,再根据now的值调用各个窗口的函数,该循环是系统的核心。当然,当窗口状态now被标记为m_exit时,退出该循环,即意味着退出系统。通过状态判断来输出各个窗口有一个好处,那就是可以方便地在各个窗口间调换:当从一个窗口退出时,根据选择将窗口状态now设置为其他窗口的状态,只要该状态不是m_exit,就可以在main()中下一轮循环中执行相应的窗口。一般由主窗口跳到其他窗口的选择是最多的,而其他窗口在结束时将窗口状态now标记为main_w以返回主窗口,也有标记成m_exit的情况以直接退出系统。
- 代码重用。本程序中大量重复的多选项的选择操作(在0,1,2,…,n中选择)、Yes/No选择,与即时保存数据库文件等操作各只用一个函数实现,在需要的时候调用这些函数;比如多项操作选择函数,先在Database.h头文件中定义void MyChoice(int csmall, int cbig);其中csmall 是选项容许的最小值,cbig是选项容许的最大值,当用户的选项超出最小值到最大值的范围时,就会处理这个错误的输入,这样就可以根据每个窗口中列出的选项灵活地使用该函数来选择,再根据全局变量中的mychoice变量(在本函数中的选择结果保存在此变量中)来运行相应选项对应的函数。在编写程序时考虑代码重用带来了巨大的便利,让我把更多的精力投入到其他函数的设计上。
- 非法输入的处理是本系统的一大特色。本系统对于不符合规范的输入有过滤和重新输入两种处理方式。当一次输入带有空格的内容时,一般会把空格后的的内容当成下一次输入,造成不可测的混乱,于是在每次输入后采用guolv()这个函数进行过滤,原理是
while(std::cin.get() != ‘\n’);
将第一个空格后到回车之间的输入都过滤掉了。Guolv()函数的重用率也是非常高的。除了过滤之外,选择选项(0,1,2,…,n或者[Y/N])时对于不符合选项的输入采取了提示重新输入的办法,这样做的基础是将输入当成是一个std::string类的输入,再对该std::string变量进行合法性分析。非法输入的处理健壮了程序,使用户只能按规定输入,避免不可知的错误。
- 操作的可撤回性与连惯性是本系统的一大特色。
- 在设计上实现进行了下述功能,使得几乎在任何时刻都可以即时返回与撤销操作:
- 在采编未将书名、作者、出版社都没有输入完毕时,可通过输入exit返回主窗口;
- 在借阅书籍、归还、续借、逐本删除时均可输入0撤消当前操作;
- 在逐本删除功能结束后会提示是否保存,选择不保存就相当于没有进行删除操作;
- 在进行一些重要操作时会提示是否进行该操作。
如此,使人为操作造成的失误可即时撤回,提高操作的安全性。
- 在操作的连惯性方面,有如下设计:
- 在有可能需要连续操作的功能中引入循环,通过判断是否继续(Yes_or_Not())来决定下一步是退出该功能还是继续该功能,如可以继续查询、继续借阅、继续归还、继续续借、继续删除……
- 将查询与借阅功能归并,在查询后提供“借阅”的选项,用户选择该选项即可马上借阅搜寻到的书籍;
- 大部分窗口和功能中都有“返回主菜单”或“退出”的选项,使用户无需一步一步返回上一级再进行其他选项或退出,一步直达。
如此,提高了系统的用户友好程度,更加方便用户使用。
- 在设计上实现进行了下述功能,使得几乎在任何时刻都可以即时返回与撤销操作:
- 管理员模式[*]。管理员模式下可以直接操作数据库文件,即把数据库文件打开,直接修改其中的数据,这个也只有管理员才看得懂了。打开之前需输入管理员密码,关闭数据库文件后提示“数据库已改变,是否重新载入?”,这么做也是减小修改失误带来的风险。输入密码也是删除书目[*]和清空数据库[*]这两个重要功能的必经之路。
五、实验内容
非法输入的纠正:当输入非指定选项时提示重新输入
带空格输入的过滤:只认定第一个输入是有效的
这里输入了一串从1-10的选项,但只有第一项是有效的,所以选择了第一项“采编”,如下图。
功能的连惯性:执行功能后提示是否继续
功能的可撤消性:
输入0返回:如下图,不小心进入了借阅选项后可以输入0返回
删除书目的可撤消性:如下图,在误删书目后,可以选择不保存来撤消之前的删除操作
从系统外部打开数据库文件:不显示数据库内容。(若想打开,需在cmd下用
此命令打开)
主窗口:与其他窗口同级,只是使用率大。
采编:当输错了书籍信息时,可通过即时输入exit返回(注意最迟是在输入出版社名称时可撤回,到了之后选择书籍类别就不能撤回了)
查询:查询成功之后会显示书目的详细信息,包括书名、作者、出版社、借阅情况等
书名是模糊查找方式:如下图,按书名查找,输入“自然”二字可以查找到《自然哲学中的数学原理》一书,此外书名、作者、出版社查询均不区分大小写
按类别查找:可通过输入相应书目类别的序号进行查找,注意最多只能显示10本相应的书目
借阅:在查询操作之后,选择2即可借阅,也可继续查询、返回主界面或者退出等
还书:需要输入借阅者姓名,之后按本显示读者的借阅情况,在输入显示的书目序号之后,该书籍即被归还(在该书籍下方会显示“已归还”),之后提示是否继续归还,输入0也可退出归还
续借:与归还类似,需要输入读者的姓名,之后列出读者所有借阅过的书籍,通过输入书籍的序号对相应书籍进行续借,续借后可选择是否继续续借
删除:
按本删除:在输入管理员密码密码列出了所有
清空数据库:像这种重要的操作,首先提示是否继续,再输入管理员密码,如果密码错误,则可输入exit返回
管理员模式:输入管理员密码,打开数据库文件,可在此文件中修改管理员密码,在此模式下一定要谨慎修改内容,防止数据被改得不完整而造成重新载入时系统崩溃
在关闭数据库文件时,系统提示是否重新载入信息,若不重新载入,则之前的修改作废,数据库仍然为修改前的内容
若重新载入,则提示是否继续,若选择退出,可直接退出系统,否则返回主菜单
代码重用:下述代码(函数)的重用率极高
void MyChoice(int csmall, int cbig);
//选择选项,修改的全局变量:mychoice
void guolv();
//每次输入后,过滤空格后的输入
void Yes_or_Not();
//多用在是否继续的判断上,全局变量:yes
void Database::saveNoneWord();
//各种操作后即时将数据保存至数据库
六、实验总结
本实验最大的体会是自顶向下设计的设计方式,首先明确各个功能,再明确各个功能的函数实现,由总体规划(各个窗口的显示、转换)到各个细节的实现(各个功能用类,函数来实现)。在细节上采用代码重用使设计更加简洁高效。
学会了使程序输入更加健壮的做法。通过过滤、判断std::string类输入是否符合规范来纠正输入错误。一系列用户友好设计,连惯性、可即时撤回性的实现,让用户使用起来更加方便。
在时间复杂度方面,主要体现在查找书籍方面,在查找书籍上使用线性探索,时间复杂度为O(N),空间复杂度为O(1)。储存上的复杂度为O(M),M为总书本数量。
七、参考文献
- C++string中用于查找的find系列函数浅析 - 同勉共进 - 博客园
- c++输入隐藏密码的实现 - CSDN博客
- C++中如何用cout实现输出的填充,宽度,对齐及其精度控制 - PrConstantin - CSDN博客
- NTFS交换数据流隐写的应用 - Chesky - 博客园
- string::find - C++ Reference
- 隐写NTFS STREAM - CSDN博客
八、代码
// Database.h
#ifndef DATABASE_H_
#define DATABASE_H_
#include <string>
#include <windows.h>
#include <time.h>
#include <conio.h>
#define PWDLENTH 100
#define MAX_NUM 200
#define MAX_BOOK 100000
#define PAUSETIME 1000
#define FILEPATH "database.dat:data.txt"
#define UNSECRET_PATH "database.dat"
#define EXIT "exit"
#define TYPE_NUM 12
#define FIND_TYPE 10
#define DEFAULT_PASSWORD "12345678"
typedef struct kuChun{
bool used;
std::string reader;
int year, month, day;
}kuChun;
typedef struct Book
{
std::string name;
std::string writer;
std::string company;
int type;
int ku_num;
int left;
kuChun ku[MAX_NUM];
}Book;
typedef struct Record{
int book_no;
int ku_no;
}Record;
class Database
{
private:
Book book[MAX_BOOK];
int book_num;
int book_all;
int book_left;
void delayThirty(int, int);
Record book_record[MAX_NUM];
int record;
public:
int getBook_num();
int getBook_all();
int getBook_left();
int find_add[MAX_BOOK];
int find_num;
void Initial();
void addBook();
void saveData();
void saveNoneWord();
void borrowBook();
void backBook();
void delayBook();
void find_name();
void find_writer();
void find_company();
void find_n_w();
void find_n_c();
void find_w_c();
void find_n_w_c();
void find_type();
void clear();
void clear_all();
void showInfo(int);
void showRecord(int, int);
};
void jiXu();
void jiXu_find();
void jiXuChoice(int, int);
void MyChoice(int, int);
void guolv();
void Yes_or_Not();
void inputPwd(std::string &, int);
std::string trans(std::string);
std::string yes;
Database data;
int mychoice;
int jixu_choice;
enum state{mainw, find_borrow, add, del, back_delay, m_exit, admin};
state now;
enum find_state{finding, f_exit};
find_state now_find;
enum back_state{backing, b_exit};
back_state now_back;
enum del_state{deling, d_exit};
del_state now_del;
std::string types[TYPE_NUM] = {
"[1.科学百科]", "[2.历史文献]", "[3.小说 ]", "[4.期刊报纸]",
"[5.计算机 ]", "[6.数理 ]", "[7.哲学 ]", "[8.天文 ]",
"[9.机械 ]", "[10.经济 ]", "[11.语言 ]", "[12.其它 ]"
};
SYSTEMTIME st = {
0};
std::string add_name;
std::string add_writer;
std::string add_company;
std::string PASSWORD;
#endif // DATABASE_H_
// Database.cpp
#include "Database.h"
#include <iostream>
#include <fstream>
#include <cstdlib>
int Database::getBook_num(){
return book_num;
}
int Database::getBook_all(){
return book_all;
}
int Database::getBook_left(){
return book_left;
}
std::string trans(std::string origin){
for (int i = 0; i < origin.length(); i ++)
if (origin[i] >= 'a' && origin[i] <= 'z')
origin[i] -= 'a' - 'A';
return origin;
}
void guolv(){
while (std::cin.get() != '\n');
}
void MyChoice(int csmall, int cbig){
std::string str;
while(std::cin >>