实现图书管理系统功能:基本要求如下:
管理图书信息化管理,使用对象为普通用户、管理员。实现对用户(普通用户、管理员)的账号管理、图书上新、图书查询、图书下架、图书信息修改等的功能。
-
用户信息导入:用户名、账号、密码、账号类型
-
图书信息导入:编号、书名、作者、出版商、出版日期、价格、库存
-
帐号登陆:登录成功调用对应函数、登陆失败错误处理
-
用户信息管理:
管理员:新增用户、删除用户、修改用户账号信息
用户:修改密码
-
书信息管理:
管理员:图书上架、图书下架、图书搜索、图书信息修改、图书列表浏览
用户:图书查询
具体分析如下:
程序功能图:
![](https://i-blog.csdnimg.cn/blog_migrate/b441282aa9c94c5a2d89a1c996d9936c.png)
程序流程图:
![](https://i-blog.csdnimg.cn/blog_migrate/4b31e37da5a4a314e3ba5f0c56ae90d6.png)
运行操作
-
用户可以自行编译 main.c 文件,也可以直接运行可执行程序 main.exe。
-
帐号信息、图书信息均以明文存放分别存放在:userinfo.txt、bookinfo.txt。
![](https://i-blog.csdnimg.cn/blog_migrate/aba37f40fde9df03099a354c4e5ee1c4.png)
代码分析:代码运行环境为:code blocks 或dev c++
数据导入
将以文件形式保存的用户信息、图书信息读入到内存中。这里采取的基于结构体的动态链表,存储导入的信息。加载信息完成后,输出字符画以及欢迎语。
帐号登录
为确保用户正确填写帐号密码信息,显示提示信息:“请输入用户名和密码或帐号和密码”。这里有两种情况:登录成功、登录失败(含输入信息错误)。当登录成功时,进入对应用户类型的操作界面。登录失败,则给出提示:“输入信息错误,退出请按0,再次输入按1”,以询问用户是否再次尝试登录。登录失败的异常处理,使用 while 循环,直到用户选择退出程序。和密码”。这里有两种情况:登录成功、登录失败(含输入信息错误)。当登录成功时,进入对应用户类型的操作界面。登录失败,则给出提示:“输入信息错误,退出请按0,再次输入按1”,以询问用户是否再次尝试登录。登录失败的异常处理,使用 while 循环,直到用户选择退出程序。
用户在看到提示信息输入用户信息之后,将获取到的信息传入密码校验函数,基于密码校验函数返回值,判断登录成功状态。查询登录用户结构体内的用户类型 type 的值,以此判断进入的用户操作界面。
管理员入口
输出操作选项类别:用户管理、图书信息管理、注销。获取管理员操作选项后,使用 switch 进入对应功能函数。进入对应的功能函数后,显示此选项下提供的功能。用户管理选项功能:添加账号、删除账号、输出已有账号、重置用户密码、返回上一层菜单。图书信息管理选项功能:新书上架、输出图书信息、查询、旧书下架、修改图书信息、返回上一层菜单。
用户管理
添加账号
进入函数后,首先以重新加载用户信息的方式,寻找到用户信息的链表的链尾内存地址。为确保用户正确填写新增用户信息,显示提示信息:“请依次输入用户名、账号、密码和类别,输入#表示输入结束”,以及给出示例模版:“输入示例:张三1 111111 用户”。
删除帐号
为确保用户正确填写删除用户信息,显示提示信息:“请输入待删除的读者用户名或账号信息”。获取到帐号信息后,进行校验帐号存在性,如果不存在,则输出错误提示:“查无读者信息,无法删除”。存在则进项该用户信息堆块脱链,并释放,完成后输出提示信息:“读者信息已删除”。结束操作后,将新的链表保存到文件中。
输出已有帐号
显示输出帐号功能菜单,用 switch 实现选择功能。while 循环输出直到链尾,其中加入判断条件 type 值,从而实现分别输出管理员、普通用户帐号信息。
重置用户密码
为确保用户正确填写删除用户信息,显示提示信息:“请输入待删除的读者用户名或账号信息”。获取到帐号信息后,遍历链表进行校验帐号存在性,如果不存在,则输出错误提示:“未找到对应的用户名或账号”。存在则进项该用户信息进行覆写,完成后输出提示信息:“已重置用户密码”。结束操作后,将新的链表保存到文件中。
图书管理
新书上架
进入函数后,首先以重新加载书籍信息的方式,寻找到数据信息的链表的链尾内存地址。为确保用户正确填写新增数据信息,显示提示信息:“请依次输入图书编号、书名、作者、出版社、类别、出版年月日、价格、库存,输入-1表示输入结束”,以及给出示例模版:“输入示例:201501 西游记 吴承恩 中华 名著 19580624 56 59”。使用 while 循环录入信息,直到用户结束输入。结束录入后,将新的链表保存到文件中。
输出图书信息
判断链表指针的 next 值,确定是否有图书信息存在链表中。
查询
显示输出查询功能菜单,用 switch 实现选择功能。遍历链表中的书名或作者名寻找书籍,并输出。当没有对应书籍信息时,输出错误提示:"该书信息不存在”。
旧书下架
显示输出查询功能菜单,用 switch 实现选择功能。遍历链表中的书名或编号寻找书籍,若存在则将该堆块脱链并释放,并输出修改后的新链表,保存链表到文件中;不存在给出错误提示信息:“该图书信息不存在”。
修改图书信息
为确保用户正确填写新增数据信息,显示提示信息:“请输入要修改图书的编号:”。获取编号后,先判断链表是否存在图书信息,然后开始遍历链表,若图书不存在,输出错误提示信息:“图书信息不存在”,若找到图书信息,则输出提示:“该图书的信息已查询到,信息如下”并输出修改书籍原始数据。修改书籍信息采取逐项询问、判断后确定修改信息。遍历询问全部信息后,输出修改后的图书信息。
用户入口
更换密码
为确保用户正确填写新增数据信息,显示提示信息:“请输入您的用户名或账号:”。获取到用户名或帐号后,进入到 while 循环。首先验证帐号的存在性,若帐号不存在,则显示错误提示信息:“账号信息不存在”;帐号存在则要求输入修改帐号的密码,并显示提示:“请输入您原来的密码”。获取密码之后,与当前堆块的 password 比对,正确则给出开始修改密码的提示:“输入您的新密码”,对堆块的 password覆写完成后,输出成功修改提示:“密码修改成功”。密码校验错误则询问是否再次验证:“密码错误,重新输入请按1, 退出请按2”,直到用户选择退出才返回上一级菜单。
查询图书信息
用户与管理员使用相同的查询图书函数。
详细设计
该程序中:共自定义2个结构体,25个函数;共使用4个全局变量。
main函数:
在主函数 main 中,按照图示顺序依次调用函数。欢迎语部分结构简单,因此直接集成在 main 中,不单独自定义子函数。帐号密码输入部分,利用 printf 给予用户操作提示,使用 scanf 收集用户的输入值保存到变量 id、password 中,传到到login 函数中进行密码校验等操作。当密码错误时,在 login 函数内部对错误进行处理。显示错误提示信息:“输入信息错误,退出请按0,再次输入按1”,询问用户是否重新输入帐号密码。
注销帐号,则程序流回到欢迎语,开启新一轮循环。
运行效果展示:(功能太多,不一 一展示!需要代码仔细看截图!)
管理员登录:(登录的账号密码均从文件中读取)
![](https://i-blog.csdnimg.cn/blog_migrate/6bca4bde0e14e5cc4934203ddb04049f.png)
添加或删除读者用户:
![](https://i-blog.csdnimg.cn/blog_migrate/6c1ff49c34a032018a1b69ff6a9a6e75.png)
显示用户或管理员:
![](https://i-blog.csdnimg.cn/blog_migrate/923d1d419d92d57ac133b8183312a88d.png)
查询所有图书:
![](https://i-blog.csdnimg.cn/blog_migrate/d49612ac445f95a96a59181cd6ae9d5e.png)
添加图书:
![](https://i-blog.csdnimg.cn/blog_migrate/5700cc2d6be71c3cc9650651bcae01eb.png)
删除图书:
![](https://i-blog.csdnimg.cn/blog_migrate/f165d41ccc9a26b52a0db6889e7d17bd.png)
账号注销:
![](https://i-blog.csdnimg.cn/blog_migrate/c053c203f0697e58954f7ae45bc7b4ea.png)
代码实现:
完整代码请访问如下链接:
腾讯文档腾讯文档-在线文档https://docs.qq.com/doc/p/71239d69a76f56cf1521717ae6b22c27cf876f10
//声明全局函数
void load(book_pointer book_head,student_pointer student_head);//将文件中的信息加载到链表中
void login(student_pointer k,char str1[],char str2[]);//检验账号信息函数
int compare(char k[],char m[], char n[], char str[], char password[]);//密码校验
void librarian(book_pointer book_head, student_pointer student_head);//管理员入口函数
void user_management_system(student_pointer student_head);//用户管理函数
void reader(book_pointer book_head, student_pointer student_head);//普通读者函数
void change(student_pointer student_head);//修改用户密码
void adduser(student_pointer student_head);//新增用户
void deleteuser(student_pointer student_head);//删除用户
void print(student_pointer student_head);//显示读者信息入口函数
void printuser(student_pointer student_head);//输出用户信息
void printadmin(student_pointer student_head);//输出管理员信息
void save(student_pointer student_head);//保存用户信息
void password(student_pointer student_head);//读者更改密码
void book_management_system(book_pointer book_head);//图书信息管理函数
void newbook(book_pointer book_head);//添加图书信息
void Save(book_pointer book_head);//将图书馆信息导入文件中
void Output(book_pointer book_head);//输出图书信息
void oldbook(book_pointer book_head);//旧书下架
void titledelete(book_pointer book_head);//按书名删除书籍
void iddelete(book_pointer book_head);//按编号删除书籍
void search(book_pointer book_head);//查询图书信息
void titlesearch(book_pointer book_head);//书名查找
void authorsearch(book_pointer book_head);//作者名查找
void modify(book_pointer book_head);//图书信息修改
void login(student_pointer k,char id[],char password[])
{
while(k)
{
b=0;//复位帐号登录状态
b=compare(k->name,k->id,k->password,id,password);
if(b==1)
{
if(strcmp(k->type,"管理员")==0)
{
librarian(book_head, student_head);//图书管理员函数
}
if(strcmp(k->type,"用户")==0)
{
reader(book_head, student_head);//普通用户函数
}
break;//直接跳出循环,实现帐号注销
}
k=k->next;
}
int b1;//用于记录是否重新输入状态码
while(b!=1)//是否注销帐号状态码
{
printf("输入信息错误,退出请按0,再次输入按1:\n");
scanf("%d",&b1);
if(b1==1)//重新输入
{
printf("请重新输入信息:\n");
printf("用户名或账号:");
scanf("%s",id);//id是字符串,即本身就是指针
printf("密码:");
scanf("%s",password);
k=student_head;//重置指针
while(k)
{
b=0;
b=compare(k->name,k->id,k->password,id,password);
if(b==1)//重新输入
{
if(strcmp(k->type,"管理员")==0)
{
librarian(book_head, student_head);//管理员函数
}
if(strcmp(k->type,"用户")==0)
{
reader(book_head, student_head);//普通读者函数
}
break;//实现帐号注销
}
k=k->next;
}
}
else if (b1==0)//退出
{
exit(0);
}
else//异常处理
{
printf("选择选项不存在\n\n");
}
}
}
int compare(char k[],char m[], char n[], char str[], char password[])
{
if((strcmp(str,k)==0||strcmp(str,m)==0)&&(strcmp(password,n)==0))
{
printf("输入信息正确,即将进入系统\n\n");
return 1;
}
return 0;
}
void librarian(book_pointer book_head, student_pointer student_head)//图书管理员函数
{
do{
printf("******************************************\n");
printf("* 1:用户管理 *\n");
printf("* 2:图书信息管理 *\n");
printf("* 3:注销 *\n");
printf("******************************************\n\n");
printf("请输入你要进行的项目选项\n");
scanf("%d", &select);
switch(select)
{
case 1:
user_management_system(student_head);//用户管理函数
break;
case 2:
book_management_system(book_head);//图书信息管理函数
break;
case 3:
printf("\n\n");
break;
default:
printf("输入选项不存在!\n\n");
}}while(select!=3);
}
void user_management_system(student_pointer student_head)//用户管理函数
{
do{
printf("************************************\n");
printf("* 1:添加账号 *\n");
printf("* 2:删除账号 *\n");
printf("* 3:输出已有账号 *\n");
printf("* 4:重置用户密码 *\n");
printf("* 5:返回 *\n");
printf("************************************\n\n");
printf("请输入你要进行的项目选项\n");
scanf("%d", &select);
switch(select)
{
case 1:
adduser(student_head);//添加账号
break;
case 2:
deleteuser(student_head);//删除账号
break;
case 3:
print(student_head);//输出已有账号
break;
case 4:
change(student_head);//重置用户密码
break;
case 5:
printf("\n\n");
break;
default:
printf("输入选项不存在!\n");
}}while(select!=5);
}
void adduser(student_pointer student_head)
{
student_pointer p1,p2=student_head;
char name[10];
char id[10];//char id[10];
char password[8];//char password[8];
char type[10];
FILE *fp;
//以重新load用户信息方式找到链表末尾
//这种方式浪费内存空间
if((fp=fopen("userinfo.txt","r+"))==NULL)
{
printf("File Open Error!\n");
exit(0);
}
while(fscanf(fp,"%s %s %s %s",name,id,password,type)!=EOF)
{
p1=(student_pointer)malloc(sizeof(student));
strcpy(p1->name,name);
strcpy(p1->id,id);
strcpy(p1->password,password);
strcpy(p1->type,type);
p2->next=p1;
p2=p1;//最后两个指针指向最后一个堆块
}
printf("请依次输入用户名、账号、密码和类别,输入#表示输入结束\n");
printf("输入示例:张三 1 111111 用户\n");
while(scanf("%s",&name)!=EOF&&strcmp(name,"#")!=0)
{
p1=(student_pointer)malloc(sizeof(student));
scanf("%s",&id);
scanf("%s",&password);
scanf("%s",&type);
strcpy(p1->name,name);
strcpy(p1->id,id);
strcpy(p1->password,password);
strcpy(p1->type,type);
p2->next=p1;
p2=p1;
}
p2->next=NULL;
printf("已有普通读者用户信息:\n");
printuser(student_head);
save(student_head);
}
void print(student_pointer student_head)//显示已有读者信息
{
do{
printf("************************************\n");
printf("* 1:输出管理员账号 *\n");
printf("* 2:输出用户账号 *\n");
printf("* 3:返回 *\n");
printf("************************************\n\n");
printf("请输入你要进行的项目选项\n");
scanf("%d", &select);
switch(select)
{
case 1:
printadmin(student_head);//输出管理员信息
break;
case 2:
printuser(student_head);//输出用户信息
break;
case 3:
printf("\n\n");
break;
default:
printf("输入选项不存在!\n");
break;
}}while(select!=3);
}
void printadmin(student_pointer student_head)//输出管理员信息
{
student_pointer p=student_head->next;
int i=0;
while(p)
{
if(strcmp(p->type,"管理员")==0)
{
printf("%s %s %s\n",p->name,p->id,p->password);
i++;
}
p=p->next;
}
if(i==0)
printf("没有管理员信息!");
}
void printuser(student_pointer student_head)//输出用户信息
{
student_pointer p=student_head->next;
int i=0;
while(p)
{
if(strcmp(p->type,"用户")==0)
{
printf("%s %s %s\n",p->name,p->id,p->password);
i++;
}
p=p->next;
}
if(i==0)
{
printf("没有用户信息!");
}
}
void book_management_system(book_pointer book_head)//图书信息管理函数
{
do{
printf("**********************************************\n");
printf("* 欢迎进入图书管理员界面 *\n");
printf("* 1:新书上架 *\n");
printf("* 2:输出图书信息 *\n");
printf("* 3:查询 *\n");
printf("* 4:旧书下架 *\n");
printf("* 5:修改图书信息 *\n");
printf("* 6:退出 *\n");
printf("**********************************************\n");
printf(" 请输入你的选择:\n");
scanf("%d",&select);
switch(select)
{
case 1:newbook(book_head);//新书上架
break;
case 2:Output(book_head);//图书输出
break;
case 3:search(book_head);//图书查找
break;
case 4:oldbook(book_head);//旧书下架
break;
case 5:modify(book_head);//修改图书信息
break;
case 6:printf("\n\n");
break;
default:
printf("选项不存在!\n");
}}while(select!=6);
}
void newbook(book_pointer book_head)//添加图书信息
{
book_pointer p1,p2=book_head;
char name[20],author[20],publisher[20],type[20];
int id,pubtime,price,Num;
FILE *fp;
if((fp=fopen("bookinfo.txt","r"))==NULL)
{
printf("File Open Error!\n");
exit(0);
}
while(fscanf(fp,"%d%s%s%s%s%d%d%d",&id,name,author,publisher,type,&pubtime,&price,&Num)!=EOF)
{
p1=(book_pointer)malloc(sizeof(book));
p1->id=id;
strcpy(p1->name,name);
strcpy(p1->author,author);
strcpy(p1->publisher,publisher);
strcpy(p1->type,type);
p1->pubtime=pubtime;
p1->price=price;
p1->Num=Num;
p2->next=p1;
p2=p1;
}
printf("请依次输入图书编号、书名、作者、出版社、类别、出版年月日、价格、库存,输入-1表示输入结束\n");
printf("输入示例:201501 西游记 吴承恩 中华 名著 19580624 56 59\n");
while(scanf("%d",&id),id!=-1)
{
p1=(book_pointer)malloc(sizeof(book));
scanf("%s%s%s%s%d%d%d",&name,&author,&publisher,&type,&pubtime,&price,&Num);
p1->id=id;
p1->pubtime=pubtime;
p1->price=price;
p1->Num=Num;
strcpy(p1->name,name);
strcpy(p1->author,author);
strcpy(p1->publisher,publisher);
strcpy(p1->type,type);
p2->next=p1;
p2=p1;
}
p2->next=NULL;
Save(book_head);
printf("现在的图书信息为:\n");
Output(book_head);
}
void Save(book_pointer book_head)//将图书馆信息导入文件中
{
FILE *fp;
book_pointer p;
if((fp=fopen("bookinfo.txt","w"))==NULL)
{
printf("File Open Error!\n");
exit(0);
}
for(p=book_head->next;p;p=p->next)
{
fprintf(fp,"%d%10s%10s%10s%10s%10d%8d%8d\n",p->id,p->name,p->author,p->publisher,p->type,p->pubtime,p->price,p->Num);
}
fclose(fp);
}
void Output(book_pointer book_head)//输出图书信息
{
book_pointer p=book_head->next;
if(p==NULL)
{
printf("没有图书信息\n");
}
else
{
printf("编号 书名 作者 出版社 类别 出版年月日 价格 库存\n");
while(p)
{
printf("%d%8s%10s%8s%8s%10d%4d%8d\n",p->id,p->name,p->author,p->publisher,p->type,p->pubtime,p->price,p->Num);
p=p->next;
}
}
}
void search(book_pointer book_head)//查询图书信息
{
do{
printf("***********************************\n");
printf("* 1:按书名查找 *\n");
printf("* 2:作者名查找 *\n");
printf("* 3:返回 *\n");
printf("***********************************\n\n");
printf(" 请输入你要进行的项目选项\n");
scanf("%d", &select);
switch(select)
{
case 1:
titlesearch(book_head);//书名查找
break;
case 2:
authorsearch(book_head);//作者名查找
break;
case 3:
printf("\n\n");
break;
default:printf("选项不存在\n");
}}while(select!=3);
}
常见问题及解决方案
-
问题:提示“File Open Error!”。
解决方案:检查 main.exe 所在目录下是否存在 userinfo.txt、bookinfo.txt 这两个文件,如果没有请手动新建。
-
问题:忘记密码怎么找回。
解决方案:打开程序根目录下的 userinfo.txt ,密码以明文保存。
-
问题:源码 main.c 打开中文乱码。
解决方案:使用 gb2318 编码格式重新打开。
已知缺陷:
-
管理函数中的添加账号等需要获取链表链尾地址的函数,获取函数方式是重建链表,导致原有堆块未被释放而留存在内存中。如果多次进行操作,可能存在内存溢出风险。
-
帐号、图书信息以明文形式保存在本地文件中。
-
普通用户修改密码的功能函数,设计存在逻辑缺陷。假如 A 与 B 密码相同,A 登录后,在使用改密码功能时,输入 B 帐号与密码也能够操作成功。
-
登录、密码修改函数模块,均为设置尝试次数,可被爆破。
-
具有删除功能的函数模块,在释放堆块后,未将操作指针归零,或存在 UAF 漏洞。