1、用C+语言实现图书管理系统摘 要 图书管理系统主要是对图书的录入、读者借阅、读者归还等功能进行实现。本课程设计的系统开发平台为Windows XP,程序设计语言为C+,程序运行平台为Windws98/2000/XP/Seven。在程序设计中采用了B-树方法提高书籍的查找速度。关键词 程序设计;图书管理系统; C+;数据结构;B-树1 索引1.1课程设计目的设计一个小型的图书管理系统,可以实现新增图书,读者借阅,读者归还等功能。1.2.系统性能要求能较快的查到所要查找的图书;能准确统计当前每种书的库存,以确定此书是否可以外借;并且对外借的图书进行管理,记录借出时间、应还时间等。1.3.功能的实现
2、1) 新书入库:确定书号后,登记到图书帐目表中,如果表中已有,则只将库存量增加;2) 借阅:如果一种书的现存量大于0,则借出一本,登记借阅者的书证号和归还期限,改变现存量;3)归还:注销对借阅者的登记,改变该书的现存量。2 系统详细设计及实现1.所用的知识体系在整个程序的设计过程当中,用到了C+的一些基础知识,面向对象的思想和结构化的程序设计思想。数据结构的B-树建立索引,用索引提高查找的效率等。主程序输入显示借阅添加加查找退出图2.1 系统功能组成框图显示库存借出图书新书入库书号查找归还归还图书2.系统功能组成框图排序3. 系统功能模块划分 摸块保称功能说明1系统管理显示库存,借阅,归还2图
3、书管理图书的添加,查询等操作 3借还书管理对每次借书信息的添加,删除等操作4.流程图4.1录入图书信息录入图书信息判断是否已经存在该图书在原有的记录上加上现有的图书数量Y向系统中加入新纪录N4.2借阅图书 输入要借阅的图书信息判断图书是否存在输出提示信息告诉读者图书不存在N处理借阅功能,将该图书数量减一Y判断图书数目是否大于0提示读者该图书都已借出NY4.3归还图书输入图书和读者信息处理归还图书功能,清读者的借阅记录,将图书的在库数加一书号排序书名排序5 功能实现51 运行程序的主界面图51 操作界面5.2 新书入库功能的操作界面图5-2新书入库5.3 查询数据的界面图5-3查询书籍5.4 查
4、询所有书籍的界面图 5-4显示库存5.5 图书借阅的界面图5-5借阅书籍5.6 还书的界面图5-6还书3 参考文献 1谭浩强C语言设计(第三版)清华大学出版社2 严蔚敏 吴伟民 数据结构(C语言版) 清华大学出版社3 谭浩强 C+ 程序设计清华大学出版社4参考网址5参考网址附录#include#include#include#include#include/定义局变量#define N 10000 /表示状态的字段#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFL
5、OW -2typedef int Status;typedef char *string ;#define m 3 /B-树的阶,设为/ 借阅者的结构体typedef struct Userunsigned int number; /借书证号码int year;int month;int day;/借书时间int dyear;/截至日期的年int dmonth;/截至日期的月int dday;/截至日期的日struct User *next; /下一个借阅者User; /定义用户的的信息/书的结构体 struct Bookunsigned int key; /图书的书号 char bname2
6、0; / 书名 char writter20;/ 著者 unsigned int left; / 现存量 unsigned int total; / 总存量User *user;/借阅该书的人bN;/定义书的信息/B- 树的存储结构typedef Book KeyType;typedef struct BTNodeint keynum; /结点中关键字个数,即结点的大小struct BTNode *parent; /指向双亲结点KeyType keym + 1; /关键字向量,号单元未用struct BTNode *ptrm + 1; /子树指针向量BTNode,*BTree;/查找结果的存储
7、结构体typedef structBTNode *pt; /指向找到的结点 int i; /1m,在结点中的关键字序号int tag; /B- 树的查找结果类型Result;BTree root = NULL; /树根/*函数声明部分/输入书的具体信息void InBookMess(KeyType &book);/输入书的关键字void InBookKey(KeyType &book);/显示书的具体信息,如果书存在就显示void ShowBookMess(Book book);/显示一个结点中所包含的全部信息,显示单个结点void ShowBTNode(BTree p);/显示,以层次的方法
8、显示树的结点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,
9、 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
10、 save(BTree p)/保存模块程序 FILE *fp;/定义文件指针if (fp=fopen(book.txt,wb)=NULL )/判断文件的存在,若非空,将fp指向filename中记载的文件名的文件printf(创建文件失败!nn);/打印出错提示getchar();return; for(int i = 1; i keynum; i+)fprintf(fp,%d %s %s %d %d n,p-keyi.key,p-keyi.bname,p-keyi.writter,p-keyi.left,p-keyi.total);fclose(fp);/关闭文件void temp(BTre
11、e T)int i;if(T) save(T);/保存这个结点的全部值for(i=0; ikeynum; i+) /使用递归的方法显示每个结点if(T-ptri)temp(T-ptri);/读取文件void read()FILE *fp,fp1;/定义文件指针if (fp=fopen(book.txt,rb)=NULL & (fp=fopen(user.txt,rb)=NULL)/判断文件的存在,若非空,将fp指向filename中记载的文件名的文件printf(创建文件失败!nn);/打印出错提示getchar();return;for(int i=1;i+)/读取文件 if(fscanf(
12、fp,%d%s%s%d%d,&bi.key,&bi.bname,&bi.writter,&bi.left,&bi.total)=EOF)break;InsertBTree(root,bi);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.us
13、er;/在一个结点中查找元素,返回结点的位置int Search(BTree p, KeyType K) if(!p)return -1;int i=0; for(i = 0; i keynum & p-keyi+1.key 0 & p-keyi.key = K.key) found = TRUE; else q = p; p = p-ptri; /在另一个分支上查找 if (found) / 查找成功 R.pt = p; R.i = i; R.tag = 1; else / 查找不成功 R.pt = q; R.i = i; R.tag = 0; / 返回结果信息: K的位置(或插入位置) r
14、eturn R; /插入一条记录void Insert(BTree &q, int i, KeyType x, BTree ap) int n = q-keynum;for (int j = n; j i; j-) KeyTypeCopy(q-keyj + 1,q-keyj); /复制结点值q-ptrj + 1 = q-ptrj;KeyTypeCopy(q-keyi + 1,x);q-ptri + 1 = ap;if (ap) ap-parent = q; q-keynum+;/分离结点void split(BTree &q, int s, BTree &ap) int i,j,n = q-k
15、eynum; ap = (BTree)malloc(sizeof(BTNode); ap-ptr0 = q-ptrs; for (i = s + 1,j = 1; i keyj,q-keyi); ap-ptrj = q-ptri; ap-keynum = n - s; ap-parent = q-parent; for (i = 0; i ptri) ap-ptri-parent = ap; q-keynum = s-1;/生成一个新的树结点void NewRoot(BTree &T, BTree p, KeyType x, BTree ap) T = (BTree)malloc(sizeof
16、(BTNode); T-keynum = 1; /设置当前结点的元素个数 T-ptr0 = p; /设置左边结点的树根 T-ptr1 = ap; /设置右边的树根 KeyTypeCopy(T-key1,x); /将x 元素的结点值复制到T 的第一个元素中 /当孩子不空的时候就设置当前结点为孩子的双亲 if (p) p-parent= T; if (ap) ap-parent = T; T-parent = NULL; /当前结点的双亲为空/返回false 表示在原有结点上增加数量,返回true 表示创建了一个新的结点Status InsertBTree(BTree &T, KeyType K)
17、 / 在m阶B树T上结点*q的keyi与keyi+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 的
18、结点 i = rs.i; /元素k 在树中的位置 if(rs.tag = 1) /判断该元素在树中是否存在if(strcmp(q-keyi.bname,K.bname) != 0)printf(nt录入失败,原因:n);printf(.t书号冲突,请重新为该书编号!nn);printf(t已经存在书号为%d 的书为:n,q-keyi.key);ShowBookMess(q-keyi);return FALSE;else printf(nt该书已经存在!nn);printf(t是否增加其总量(y/n):);scanf(%s,&addnum);if(addnum = Y | addnum = y)
19、q-keyi.total += K.total; /将总量增加 q-keyi.left += K.left; /将剩余量增加 printf(nt增加总量后该书的信息如下n);/ShowBookMess(q-keyi);elseprintf(nt该书的信息如下:n);/ShowBookMess(q-keyi);ShowBookMess(q-keyi); return FALSE; /if x = K; ap = NULL; finished = needNewRoot = FALSE; while (!needNewRoot & !finished) Insert(q, i, x, ap); /
20、插入结点 if (q-keynum keys; 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;/一个结点在双亲中的位置,返回其位置iint position(BTree T)if(!T)return 0;int i = 0;if(T-parent)while(
21、i parent-keynum)if(T = T-parent-ptri)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-ptri - 1-keynum mid) /当i 大于零的时候就可以向左借temp = root-ptri - 1; /比自己小的兄弟结点p-keynum+; /增加一个结点for
22、(k = p-keynum;k 1;k-)KeyTypeCopy(p-keyk,p-keyk - 1); /将前面的结点后移一位if(p-ptr0)for(k = p-keynum;k = 1;k-)p-ptrk = p-ptrk - 1; /将要移动的结点的子结点向后移动KeyTypeCopy(p-key1,root-keyi); /将双亲的结点复制到根KeyTypeCopy(root-keyi,temp-keytemp-keynum); /将小兄弟结点的最大的那个移动到双亲中if(temp-ptrtemp-keynum) /将兄弟结点的子结点也复制过来p-ptr0 = temp-ptrte
23、mp-keynum; temp-ptrtemp-keynum-parent = p; /修改指向双亲的结点temp-ptrtemp-keynum = NULL;temp-keynum-; /将左兄弟删除一个结点return OK;if(i keynum & root-ptri + 1-keynum mid) /当i 小于最大数量的时候就可以向右借temp = root-ptri + 1;p-keynum+; /增加结点的个数KeyTypeCopy(p-keyp-keynum,root-keyi + 1); /将根结点的值复制过来KeyTypeCopy(root-keyi + 1,temp-ke
24、y1); /将右兄弟的结点复制过来for(k = 1;k keynum;k+)KeyTypeCopy(temp-keyk,temp-keyk + 1); /将后面的结点向前移动一位if(temp-ptr0)p-ptrp-keynum = temp-ptr0;temp-ptr0-parent = p; /修改指向双亲的结点for(k = 0;k keynum;k+) /将子结点向前移动temp-ptrk = temp-ptrk + 1;temp-ptrk + 1 = NULL; /将删除的结点的子结点置为空temp-keynum-; /将右兄弟删除一个结点return OK;return FAL
25、SE;/合并结点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-ptri;p-keynum+; /增加一个结点KeyTypeCopy(p-keyp-keynum,root-keyi); /将双亲的结点复制下来if(p2-ptr0)p-ptrp-keynum = p2-ptr0; /将兄弟的子结点也复制过来p2-ptr0-parent = p; /修改双亲
26、for(k = i;k keynum;k+) /将双亲的结点向前移动一位KeyTypeCopy(root-keyk,root-keyk + 1);p-keynum+;p-keyp-keynum = p2-key1;if(p2-ptr1)p-ptrp-keynum = p2-ptr1; /将兄弟的子结点也复制过来p2-ptr1-parent = p; /修改指向双亲的结点root-keynum-;free(p2);p2 = NULL;for(k = 1;k keynum;k+)root-ptrk = root-ptrk + 1; /将双亲结点子结点向前移动root-ptrk + 1 = NULL
27、;else if(i 0)p2 = root-ptri - 1;p2-keynum+;KeyTypeCopy(p2-keyp2-keynum,root-keyi); /复制根结点的值到子结点中if(p-ptr0)p2-ptrp2-keynum = p-ptr0;p-ptr0-parent = p2; /修改指向双亲的结点for(k = i;k keynum;k+)KeyTypeCopy(root-keyk,root-keyk + 1); /将结点前移root-ptrk = root-ptrk + 1; /将子结点前移root-ptrk + 1 = NULL;root-keynum-;free(
28、p); p = p2;return OK;/与右最左结点交换void exchange(BTree &T,int i)BTree p = T;User *user = NULL;if(p-ptri)p = p-ptri;while(p-ptr0)p = p-ptr0;while(T-keyi.user)user = T-keyi.user; /指向要释放的结点T-keyi.user = T-keyi.user-next; /指向下一个结点free(user); /释放借阅者的信息KeyTypeCopy(T-keyi,p-key1); /交换数据while(i keynum) /将该结点后面的数
29、据后移KeyTypeCopy(p-keyi,p-keyi + 1); /将后一个数据复制到前一个数据i+;p-keynum-; /删除结点T = p;return;/*/输入书的具体信息void InBookMess(KeyType &book)char s5;printf(t请输入书号(编号最多为位数且不大于):);doscanf(%s,s);book.key = atoi(s);if(book.key 65535)printf(t输入有误,请重新输入:);while(book.key 65535);printf(t请输入书名:);scanf(%s,&book.bname);printf(t
30、请输入作者:);scanf(%s,&book.writter);printf(t请输入总量(不能大于):);doscanf(%s,s);book.total = atoi(s);if(book.total 65535)printf(t输入有误,请重新输入:);while(book.total 65535);book.left = book.total;book.user = NULL;/book.user = (User *)malloc(sizeof(User);/输入书的关键字void InBookKey(KeyType &book)char s5;printf(t请输入书号:);dosc
31、anf(%s,s);book.key = atoi(s);if(book.key 65535)printf(t输入有误,请重新输入:);while(book.key 65535);book.bname0 = 0;book.writter0 = 0;book.total = 0;book.left = 0;/显示书的具体信息void ShowBookMess(Book book)User *temp;printf(t书号为:%3dn, book.key);printf(t书名为:%3sn, book.bname);printf(t作者为:%3sn, book.writter);printf(t剩
32、余量为:%3dn, book.left);printf(t总量为:%3dn, book.total);printf(t- 已借该书的人有-n);temp = book.user;while(temp)printf(t图书证号:%dt借书日期:%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;
33、i keynum; i+)printf(t);printf(书号为:%d , p-keyi.key);printf(书名为:%5s , p-keyi.bname);printf(作者为:%5s , p-keyi.writter);printf(剩余量为:%5d , p-keyi.left);printf(总量为:%5d, p-keyi.total);printf(n);/显示整棵树的信息void display(BTree T)int i = 0;if(T) ShowBTNode(T);/显示这个结点的全部值for(i=0; ikeynum; i+) /使用递归的方法显示每个结点if(T-pt
34、ri)display(T-ptri);/*/*用户借阅说明 *借阅登记的信息可以链接在相应的那种书的记录之后 */输入借阅的信息Status InUserMess(User *user)char s5;time_t nowtime;struct tm *timeinfo;time( &nowtime ); timeinfo = localtime( &nowtime ); printf(nt输入借阅者的信息n);printf(t请输入图书证号:);doscanf(%s,s);user-number = atoi(s);if(user-number number 65535)printf(t输入有误,请重新输入(0到