资源描述
用C++语言实现图书管理系统
摘 要 图书管理系统主要是对图书的录入、读者借阅、读者归还等功能进行实现。本课程设计的系统开发平台为Windows XP,程序设计语言为C++,程序运行平台为Windws98/2000/XP/Seven。在程序设计中采用了B-树方法提高书籍的查找速度。
关键词 程序设计;图书管理系统; C++;数据结构;B-树
1 索引
1.1课程设计目的
设计一个小型的图书管理系统,可以实现新增图书,读者借阅,读者归还等功能。
1.2.系统性能要求
能较快的查到所要查找的图书;能准确统计当前每种书的库存,以确定此书是否可以外借;并且对外借的图书进行管理,记录借出时间、应还时间等。
1.3.功能的实现
1) 新书入库:确定书号后,登记到图书帐目表中,如果表中已有,则只将库存量增加;
2) 借阅:如果一种书的现存量大于0,则借出一本,登记借阅者的书证号和归还期限,改变现存量;
3)归还:注销对借阅者的登记,改变该书的现存量。
2 系统详细设计及实现
1.所用的知识体系
在整个程序的设计过程当中,用到了C++的一些基础知识,面向对象的思想和结构化的程序设计思想。数据结构的B-树建立索引,用索引提高查找的效率等。
主程序
输入
显示
借阅
添加加
查找
退出
图2.1 系统功能组成框图
显示库存
借出图书
新书入库
书号查找
归还
归还图书
2.系统功能组成框图
排序
3 . 系统功能模块划分
摸块保称
功能说明
1
系统管理
显示库存,借阅,归还
2
图书管理
图书的添加,查询等操作
3
借还书管理
对每次借书信息的添加,删除等操作
4.流程图
4.1录入图书信息
录入图书信息
判断是否已经存在该图书
在原有的记录上加上现有的图书数量
Y
向系统中加入新纪录
N
4.2借阅图书
输入要借阅的图书信息
判断图书是否存在
输出提示信息
告诉读者图书不存在
N
处理借阅功能,将该图书数量减一
Y
判断图书数目是否大于0
提示读者该图书都已借出
N
Y
4.3归还图书
输入图书和读者信息
处理归还图书功能,清读者的借阅记录,将图书的在库数加一
书号排序
书名排序
5 功能实现
5.1 运行程序的主界面
图5—1 操作界面
5.2 新书入库功能的操作界面
图5-2新书入库
5.3 查询数据的界面
图5-3查询书籍
5.4 查询所有书籍的界面
图 5-4显示库存
5.5 图书借阅的界面
图5-5借阅书籍
5.6 还书的界面
图5-6还书
3 参考文献
[1] 谭浩强 C语言设计(第三版)清华大学出版社
[2] 严蔚敏 吴伟民 数据结构(C语言版) 清华大学出版社
[3] 谭浩强 C++ 程序设计清华大学出版社
[4]参考网址
[5]参考网址
附录
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
#include<time.h>
//定义局变量
#define N 10000
//表示状态的字段
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef char *string ;
#define m 3 //B-树的阶,设为
// 借阅者的结构体
typedef struct User{
unsigned int number; //借书证号码
int year;
int month;
int day; //借书时间
int dyear; //截至日期的年
int dmonth; //截至日期的月
int dday; //截至日期的日
struct User *next; //下一个借阅者
}User; //定义用户的的信息
//书的结构体
struct Book{
unsigned int key; //图书的书号
char bname[20]; // 书名
char writter[20]; // 著者
unsigned int left; // 现存量
unsigned int total; // 总存量
User *user; //借阅该书的人
}b[N]; //定义书的信息
//B- 树的存储结构
typedef Book KeyType;
typedef struct BTNode{
int keynum; //结点中关键字个数,即结点的大小
struct BTNode *parent; //指向双亲结点
KeyType key[m + 1]; //关键字向量,号单元未用
struct BTNode *ptr[m + 1]; //子树指针向量
}BTNode,*BTree;
//查找结果的存储结构体
typedef struct{
BTNode *pt; //指向找到的结点
int i; //1……m,在结点中的关键字序号
int tag; //B- 树的查找结果类型
}Result;
BTree root = NULL; //树根
//******************函数声明部分
//输入书的具体信息
void InBookMess(KeyType &book);
//输入书的关键字
void InBookKey(KeyType &book);
//显示书的具体信息,如果书存在就显示
void ShowBookMess(Book book);
//显示一个结点中所包含的全部信息,显示单个结点
void ShowBTNode(BTree p);
//显示,以层次的方法显示树的结点
void display(BTree T);
//复制关键字的信息
void KeyTypeCopy(KeyType &bak,KeyType k);
//查找在某个结点中的位置
int Search(BTree p, KeyType K);
//查找
Result SearchBTree(BTree T, KeyType K);
//插入
void Insert(BTree &q, int i, KeyType x, BTree ap);
//分裂结点
void split(BTree &q, int s, BTree &ap);
//生成一个新的结点
void NewRoot(BTree &T, BTree p, KeyType x, BTree ap);
//将书的信息插入到B- 树中
Status InsertBTree(BTree &T, KeyType K);
//删除树结点
Status DeleteBT(BTree &T,KeyType k);
//与右最左结点交换
void exchange(BTree &T,int i);
//用户借阅
Status BorrowBook(BTree T,KeyType k);
//注销对借阅者的登记,改变该书的显存量
Status ReturnBook(BTree T,KeyType k);
void temp(BTree T);
void save(BTree p);
/*************************************************/
void save(BTree p)//保存模块程序
{
FILE *fp;//定义文件指针
if ((fp=fopen("book.txt","wb"))==NULL )//判断文件的存在,若非空,将fp指向filename中记载的文件名的文件
{
printf("创建文件失败!\n\n"); //打印出错提示
getchar();
return;
}
for(int i = 1; i <= p->keynum; i++){
fprintf(fp,"%d %s %s %d %d \n",p->key[i].key,p->key[i].bname,p->key[i].writter,p->key[i].left,p->key[i].total);
}
fclose(fp);//关闭文件
}
void temp(BTree T)
{
int i;
if(T) {
save(T); //保存这个结点的全部值
for(i=0; i<=T->keynum; i++){ //使用递归的方法显示每个结点
if(T->ptr[i]){
temp(T->ptr[i]);
}
}
}
}
//读取文件
void read()
{
FILE *fp,fp1;//定义文件指针
if ((fp=fopen("book.txt","rb"))==NULL && (fp=fopen("user.txt","rb"))==NULL)//判断文件的存在,若非空,将fp指向filename中记载的文件名的文件
{
printf("创建文件失败!\n\n");//打印出错提示
getchar();
return;
}
for(int i=1;;i++)//读取文件
{
if(fscanf(fp,"%d%s%s%d%d",&b[i].key,&b[i].bname,&b[i].writter,&b[i].left,&b[i].total)==EOF)
{ break; }
InsertBTree(root,b[i]);
}
fclose(fp);//关闭文件
}
/**********************************************************/
//复制结点,将某个结点的值复制到另外一个值上
void KeyTypeCopy(KeyType &bak,KeyType k){
bak.key = k.key;
strcpy(bak.bname,k.bname);
bak.left = k.left;
bak.total = k.total;
strcpy(bak.writter,k.writter);
bak.user = k.user;
}
//在一个结点中查找元素,返回结点的位置
int Search(BTree p, KeyType K) {
if(!p)
return -1;
int i=0;
for(i = 0; i < p->keynum && p->key[i+1].key <= K.key; i++);
return i;
}
// 在m阶B树T上查找关键字K,返回结果(pt,i,tag)
Result SearchBTree(BTree T, KeyType K){
BTree p, q;
int found, i;
Result R;
//初始化变量
p = T;
q = NULL;
found = FALSE;
i = 0;
// 初始化,p指向待查结点,q指向p的双亲
while (p && !found) {
i = Search(p, K);
// 找到待查关键字
if (i > 0 && p->key[i].key == K.key)
found = TRUE;
else {
q = p;
p = p->ptr[i]; //在另一个分支上查找
}
}
if (found) { // 查找成功
R.pt = p;
R.i = i;
R.tag = 1;
}
else { // 查找不成功
R.pt = q;
R.i = i;
R.tag = 0;
}
// 返回结果信息: K的位置(或插入位置)
return R;
}
//插入一条记录
void Insert(BTree &q, int i, KeyType x, BTree ap) {
int n = q->keynum;
for (int j = n; j > i; j--) {
KeyTypeCopy(q->key[j + 1],q->key[j]); //复制结点值
q->ptr[j + 1] = q->ptr[j];
}
KeyTypeCopy(q->key[i + 1],x);
q->ptr[i + 1] = ap;
if (ap)
ap->parent = q;
q->keynum++;
}
//分离结点
void split(BTree &q, int s, BTree &ap) {
int i,j,n = q->keynum;
ap = (BTree)malloc(sizeof(BTNode));
ap->ptr[0] = q->ptr[s];
for (i = s + 1,j = 1; i <= n; i++,j++) {
KeyTypeCopy(ap->key[j],q->key[i]);
ap->ptr[j] = q->ptr[i];
}
ap->keynum = n - s;
ap->parent = q->parent;
for (i = 0; i <= n - s; i++)
if (ap->ptr[i])
ap->ptr[i]->parent = ap;
q->keynum = s-1;
}
//生成一个新的树结点
void NewRoot(BTree &T, BTree p, KeyType x, BTree ap) {
T = (BTree)malloc(sizeof(BTNode));
T->keynum = 1; //设置当前结点的元素个数
T->ptr[0] = p; //设置左边结点的树根
T->ptr[1] = ap; //设置右边的树根
KeyTypeCopy(T->key[1],x); //将x 元素的结点值复制到T 的第一个元素中
//当孩子不空的时候就设置当前结点为孩子的双亲
if (p)
p->parent= T;
if (ap)
ap->parent = T;
T->parent = NULL; //当前结点的双亲为空
}
//返回false 表示在原有结点上增加数量,返回true 表示创建了一个新的结点
Status InsertBTree(BTree &T, KeyType K) {
// 在m阶B树T上结点*q的key[i]与key[i+1]之间插入关键字K。
// 若引起结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树。
BTree ap;
Result rs;
BTree q;
int i;
char addnum;
int finished, needNewRoot, s;
// T是空树(参数q初值为NULL)
KeyType x;
//如果T 结点为空就生成一个新的结点
if (!T){
NewRoot(T, NULL, K, NULL);
}
else {
//查找元素k 在树中的位置
rs = SearchBTree(T,K);
q = rs.pt; //查找到包含元素k 的结点
i = rs.i; //元素k 在树中的位置
if(rs.tag == 1){ //判断该元素在树中是否存在
if(strcmp(q->key[i].bname,K.bname) != 0){
printf("\n\t录入失败,原因:\n");
printf(".\t书号冲突,请重新为该书编号!\n\n");
printf("\t已经存在书号为%d 的书为:\n",q->key[i].key);
ShowBookMess(q->key[i]);
return FALSE;
}
else
{
printf("\n\t该书已经存在!\n\n");
printf("\t是否增加其总量(y/n):");
scanf("%s",&addnum);
if(addnum == 'Y' || addnum == 'y'){
q->key[i].total += K.total; //将总量增加
q->key[i].left += K.left; //将剩余量增加
printf("\n\t增加总量后该书的信息如下\n");
// ShowBookMess(q->key[i]);
}
else{
printf("\n\t该书的信息如下:\n");
// ShowBookMess(q->key[i]);
}
ShowBookMess(q->key[i]);
return FALSE;
}
} //if
x = K;
ap = NULL;
finished = needNewRoot = FALSE;
while (!needNewRoot && !finished) {
Insert(q, i, x, ap); //插入结点
if (q->keynum < m)
finished = TRUE; // 插入完成
else { // 分裂结点*q
s = (m+1)/2;
split(q, s, ap);
x = q->key[s];
if (q->parent) { // 在双亲结点*q中查找x的插入位置
q = q->parent;
i = Search(q, x);
}
else
needNewRoot = TRUE;
} // else
} // while
if (needNewRoot) // 根结点已分裂为结点*q和*ap
NewRoot(T, q, x, ap); // 生成新根结点*T,q和ap为子树指针
}
return OK;
}
//一个结点在双亲中的位置,返回其位置i
int position(BTree T){
if(!T){
return 0;
}
int i = 0;
if(T->parent){
while(i <= T->parent->keynum){
if(T == T->parent->ptr[i])
return i; //返回当前的位置
i++;
}
}
return -1;
}
//调整树的结构
Status fix(BTree &root,BTree p){
int i = position(p); //取得p 在双亲中的位置
int mid = (m + 1)/2 - 1; //要交换的临界点
BTree temp = NULL;
int k;
if(i > 0 && root->ptr[i - 1]->keynum > mid){ //当i 大于零的时候就可以向左借
temp = root->ptr[i - 1]; //比自己小的兄弟结点
p->keynum++; //增加一个结点
for(k = p->keynum;k > 1;k--){
KeyTypeCopy(p->key[k],p->key[k - 1]); //将前面的结点后移一位
}
if(p->ptr[0]){
for(k = p->keynum;k >= 1;k--){
p->ptr[k] = p->ptr[k - 1]; //将要移动的结点的子结点向后移动
}
}
KeyTypeCopy(p->key[1],root->key[i]); //将双亲的结点复制到根
KeyTypeCopy(root->key[i],temp->key[temp->keynum]); //将小兄弟结点的最大的那个移动到双亲中
if(temp->ptr[temp->keynum]){ //将兄弟结点的子结点也复制过来
p->ptr[0] = temp->ptr[temp->keynum];
temp->ptr[temp->keynum]->parent = p; //修改指向双亲的结点
temp->ptr[temp->keynum] = NULL;
}
temp->keynum--; //将左兄弟删除一个结点
return OK;
}
if(i < root->keynum && root->ptr[i + 1]->keynum > mid){ //当i 小于最大数量的时候就可以向右借
temp = root->ptr[i + 1];
p->keynum++; //增加结点的个数
KeyTypeCopy(p->key[p->keynum],root->key[i + 1]); //将根结点的值复制过来
KeyTypeCopy(root->key[i + 1],temp->key[1]); //将右兄弟的结点复制过来
for(k = 1;k < temp->keynum;k++){
KeyTypeCopy(temp->key[k],temp->key[k + 1]); //将后面的结点向前移动一位
}
if(temp->ptr[0]){
p->ptr[p->keynum] = temp->ptr[0];
temp->ptr[0]->parent = p; //修改指向双亲的结点
for(k = 0;k < temp->keynum;k++){ //将子结点向前移动
temp->ptr[k] = temp->ptr[k + 1];
}
temp->ptr[k + 1] = NULL; //将删除的结点的子结点置为空
}
temp->keynum--; //将右兄弟删除一个结点
return OK;
}
return FALSE;
}
//合并结点
Status combine(BTree &root,BTree &p){
int k,i = position(p); //取得p 在双亲中的位置
int mid = (m + 1)/2 - 1; //交换的条件
BTree p2;
if(i == 0){ //如果是第一个位置
i = 1;
p2 = root->ptr[i];
p->keynum++; //增加一个结点
KeyTypeCopy(p->key[p->keynum],root->key[i]); //将双亲的结点复制下来
if(p2->ptr[0]){
p->ptr[p->keynum] = p2->ptr[0]; //将兄弟的子结点也复制过来
p2->ptr[0]->parent = p; //修改双亲
}
for(k = i;k < root->keynum;k++){ //将双亲的结点向前移动一位
KeyTypeCopy(root->key[k],root->key[k + 1]);
}
p->keynum++;
p->key[p->keynum] = p2->key[1];
if(p2->ptr[1]){
p->ptr[p->keynum] = p2->ptr[1]; //将兄弟的子结点也复制过来
p2->ptr[1]->parent = p; //修改指向双亲的结点
}
root->keynum--;
free(p2);
p2 = NULL;
for(k = 1;k <= root->keynum;k++){
root->ptr[k] = root->ptr[k + 1]; //将双亲结点子结点向前移动
}
root->ptr[k + 1] = NULL;
}
else if(i > 0){
p2 = root->ptr[i - 1];
p2->keynum++;
KeyTypeCopy(p2->key[p2->keynum],root->key[i]); //复制根结点的值到子结点中
if(p->ptr[0]){
p2->ptr[p2->keynum] = p->ptr[0];
p->ptr[0]->parent = p2; //修改指向双亲的结点
}
for(k = i;k < root->keynum;k++){
KeyTypeCopy(root->key[k],root->key[k + 1]); //将结点前移
root->ptr[k] = root->ptr[k + 1]; //将子结点前移
}
root->ptr[k + 1] = NULL;
root->keynum--;
free(p);
p = p2;
}
return OK;
}
//与右最左结点交换
void exchange(BTree &T,int i){
BTree p = T;
User *user = NULL;
if(p->ptr[i]){
p = p->ptr[i];
while(p->ptr[0]){
p = p->ptr[0];
}
while(T->key[i].user){
user = T->key[i].user; //指向要释放的结点
T->key[i].user = T->key[i].user->next; //指向下一个结点
free(user); //释放借阅者的信息
}
KeyTypeCopy(T->key[i],p->key[1]); //交换数据
}
while(i < p->keynum){ //将该结点后面的数据后移
KeyTypeCopy(p->key[i],p->key[i + 1]); //将后一个数据复制到前一个数据
i++;
}
p->keynum--; //删除结点
T = p;
return;
}
/*************************************************************/
//输入书的具体信息
void InBookMess(KeyType &book){
char s[5];
printf("\t请输入书号(编号最多为位数且不大于):");
do{
scanf("%s",s);
book.key = atoi(s);
if(book.key < 1 || book.key > 65535){
printf("\t输入有误,请重新输入:");
}
}while(book.key < 1 || book.key > 65535);
printf("\t请输入书名:");
scanf("%s",&book.bname);
printf("\t请输入作者:");
scanf("%s",&book.writter);
printf("\t请输入总量(不能大于):");
do{
scanf("%s",s);
book.total = atoi(s);
if(book.total < 1 || book.total > 65535){
printf("\t输入有误,请重新输入:");
}
}while(book.total < 1 || book.total > 65535);
book.left = book.total;
book.user = NULL;
//book.user = (User *)malloc(sizeof(User));
}
//输入书的关键字
void InBookKey(KeyType &book){
char s[5];
printf("\t请输入书号:");
do{
scanf("%s",s);
book.key = atoi(s);
if(book.key < 1 || book.key > 65535){
printf("\t输入有误,请重新输入:");
}
}while(book.key < 1 || book.key > 65535);
book.bname[0] = '\0';
book.writter[0] = '\0';
book.total = 0;
book.left = 0;
}
//显示书的具体信息
void ShowBookMess(Book book){
User *temp;
printf("\t书号为:%3d\n", book.key);
printf("\t书名为:%3s\n", book.bname);
printf("\t作者为:%3s\n", book.writter);
printf("\t剩余量为:%3d\n", book.left);
printf("\t总量为:%3d\n", book.total);
printf("\t------------ 已借该书的人有------------\n");
temp = book.user;
while(temp){
printf("\t图书证号:%d\t借书日期:%d年%d月%d日\t归还日期:%d年%d月%d日\n",temp->number,temp->year,temp->month,temp->day,temp->dyear,temp->dmonth,temp->dday);
temp = temp->next;
}
printf("\n");
}
//显示某个结点的信息
void ShowBTNode(BTree p) {
for(int i = 1; i <= p->keynum; i++){
printf("\t");
printf("书号为:%d ", p->key[i].key);
printf("书名为:%5s ", p->key[i].bname);
printf("作者为:%5s ", p->key[i].writter);
printf("剩余量为:%5d ", p->key[i].left);
printf("总量为:%5d", p->key[i].total);
printf("\n");
}
}
//显示整棵树的信息
void display(BTree T){
int i = 0;
if(T) {
ShowBTNode(T); //显示这个结点的全部值
for(i=0; i<=T->keynum; i++){ //使用递归的方法显示每个结点
if(T->ptr[i]){
display(T->ptr[i]);
}
}
}
}
/*****************************************************************************/
/*用户借阅说明
*借阅登记的信息可以链接在相应的那种书的记录之后
*/
//输入借阅的信息
Status InUserMess(User *user){
char s[5];
time_t nowtime;
struct tm *timeinfo;
time( &nowtime );
timeinfo = localtime( &nowtime );
printf("\n\t输入借阅者的信息\n");
printf("\t请输入图书证号:");
do{
scanf("%s",s);
user->number = atoi(s);
if(user->number < 1 || user->number > 65535){
printf("\t输入有误,请重新输入(0到
展开阅读全文