C语言数据结构-多功能图书管理系统课程设计(文末附源码)

实现图书管理系统功能:基本要求如下:

管理图书信息化管理,使用对象为普通用户管理员。实现对用户(普通用户、管理员)的账号管理、图书上新、图书查询、图书下架、图书信息修改等的功能。

  1. 用户信息导入:用户名、账号、密码、账号类型

  1. 图书信息导入:编号、书名、作者、出版商、出版日期、价格、库存

  1. 帐号登陆:登录成功调用对应函数、登陆失败错误处理

  1. 用户信息管理:

管理员:新增用户、删除用户、修改用户账号信息

用户:修改密码

  1. 书信息管理:

管理员:图书上架、图书下架、图书搜索、图书信息修改、图书列表浏览

用户:图书查询

具体分析如下:

程序功能图:

程序流程图:

运行操作

  1. 用户可以自行编译 main.c 文件,也可以直接运行可执行程序 main.exe。

  1. 帐号信息、图书信息均以明文存放分别存放在:userinfo.txt、bookinfo.txt。

代码分析:代码运行环境为: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://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);
}

常见问题及解决方案

  1. 问题:提示“File Open Error!”。

解决方案:检查 main.exe 所在目录下是否存在 userinfo.txt、bookinfo.txt 这两个文件,如果没有请手动新建。

  1. 问题:忘记密码怎么找回。

解决方案:打开程序根目录下的 userinfo.txt ,密码以明文保存。

  1. 问题:源码 main.c 打开中文乱码。

解决方案:使用 gb2318 编码格式重新打开。

已知缺陷:

  1. 管理函数中的添加账号等需要获取链表链尾地址的函数,获取函数方式是重建链表,导致原有堆块未被释放而留存在内存中。如果多次进行操作,可能存在内存溢出风险。

  1. 帐号、图书信息以明文形式保存在本地文件中。

  1. 普通用户修改密码的功能函数,设计存在逻辑缺陷。假如 A 与 B 密码相同,A 登录后,在使用改密码功能时,输入 B 帐号与密码也能够操作成功。

  1. 登录、密码修改函数模块,均为设置尝试次数,可被爆破。

  1. 具有删除功能的函数模块,在释放堆块后,未将操作指针归零,或存在 UAF 漏洞。

  • 11
    点赞
  • 174
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白不会debug

需要源码打个赏呗~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值