图书馆信息管理系统

图书管理系统的设计与实现!增删改查

预备知识:

(1)指针如何变成变量

#include 
#include


void main()
{
    int *p=NULL;
int a=1;
//1.1 用变量的地址
p=&a;
*p=1001;
printf("%d\n",*p);
//1.2 动态内存申请

p=(int *)malloc(sizeof(int));  //malloc()函数需要 stdlib.h头文件
*p=10033;
printf("%d\n",*p);
}

(2)什么是结构体

结构体是新的类型,将一些自定义的数据类型组合**打包**起来,放在堆栈中
int A=1;
float B=1.1;
char name[10];
//放入结构体中,这三个变量块就放在一起打包了

struct data
{
    int A=1;
float B=1.1;
char name[10];
};
struct data c;
//通过变量访问 c.A
//通过指针访问 struct *data pData=&c
//pData->c

(3)什么是链表

链表是用指针把一些结构体变量链接起来(结构体变量c1的成员中,会放入一个结构体变量指针 指向下一个结构体变量c2)

链表就是由结构体变量组成的线性数据结构

项目开始

1.写界面--->菜单---->子模块总览

void makeMenu()
{
    printf("-------------------------------\n");
    printf("gdut图书馆里系统\n");
    printf("\t0.退出系统\n");
    printf("\t1.登记书籍\n");
    printf("\t2.浏览书籍\n");
    printf("\t3.借阅书籍\n");
    printf("\t4.归还书籍\n");
    printf("\t5.书籍排序\n");
    printf("\t6.删除书籍\n");
    printf("\t7.查找书籍\n");
    printf("请输入(0~7)\n");
    printf("-------------------------------\n");
}




int main()
{
    makeMenu();
    return 0;
}
image-20210202174049321

2.做交互

void keyDown() //判断用户输入的子菜单数字去判断
{
    int userKey=0; //默认初始值为0退出
    scanf("%d",&userKey);//用scanf获得整数 一般是没有什么坑的
    //思考一下,判断数字是0~7的数用什么处理
    switch(userKey)
    {
        case 0://退出模块,做一个人机界面 提醒
            printf("【退出】\n");
            break;
        case 1://复制粘贴的操作,快速处理一下
             printf("【登记】\n");
            break;
        case 2:
             printf("【浏览】\n");
            break;
        case 3:
             printf("【借阅】\n");
            break;
        case 4:
             printf("【归还】\n");
            break;
        case 5:
             printf("【排序】\n");
            break;
        case 6:
             printf("【删除】\n");
            break;
        case 7:
             printf("【查找】\n");
            break;
        default:
            printf("【erro】\n");
            
    }
}

main函数里面做while(1)循环处理

int main()
{
    while(1)
    {
        makeMenu();
        keyDown();
        system("pause"); //有的编译系统会直接结束,让程序等待一下
        system("cls");//防止闪屏,在这里我们还可以将上一部分的内容清楚
    }
    return 0;
}

3.设计数据(先设计容器,再处理存取删改数据)

3.1程序用什么容器装数据(数组,或者链表)

数组是连续的内存,链表是离散的内存

image-20210201210520260

3.2数据的结构(图书的信息)

struct Node
{
    int data;
    struct Node* next; //有表头的链表
};

//创建表头: 表头就是一个结构体变量
struct Node* createHead()
{
    //动态内存申请
    struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
    //变量的基本规则,使用前必须初始化
    headNode->next=NULL;//表头可以不存数据
    return headNode;
}
//创建节点: 为插入节点做准备
//把用户的数据作为参数,传入 变为结构体变量
struct Node* createNode(int data)
{
    struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
    newNode->data=data;  //节点的数据,用来做初始化
    newNode->next=NULL;
    return newNode;
}
//打印链表
void printList(struct Node* headNode)
{
    struct Node* pMove=headNode->next; //打印表头的下一个链表的内容,我们用一个指针保存
    while(pMove)//循环指针指到NULL末尾 等价于 while(pMove!=NULL)
    {
        printf("%d\t",pMove->data);
        pMove=pMove->next;
    }
}

3.3插入,只需要一种插入方式(表头法插入)

image-20210201211521759

第一个节点叫headNode,第二个节点就叫headnode->next

新插入的节点newNode,第一步连到headNode->next
第二部headNode连到newNode
image-20210202175751991
//代码实现
newNode->next=headNode->next;
headNode->next=newNode;
测试一下
void insertNodeFromHead(struct Node* headNode, int data)
{
    struct Node* newNode=createNode(data); //用户输入的data传入createNode函数,返回一个结构体变量指针,创建一个newNode
    //必须先链接,后断开
    newNode->next=headNode->next;
    headNode->next=newNode;
}
把3.2中的代码复制到main函数之前(结构体声明,创建头节点,创建新节点,输出节点成员变量)

然后main函数增加一下代码,我们试着调试一下

    struct Node* list=createHead();
    for(int i=0;i<3;i++)
    {
        insertNodeFromHead(list, i);

    }
    printList(list);

完整main函数如下

int main()
{

    struct Node* list=createHead();
    for(int i=0;i<3;i++)
    {
        insertNodeFromHead(list, i);

    }
    printList(list);


    while(1)
    {
        makeMenu();
        keyDown();
        system("pause");
        system("cls");
        
    }
    return 0;
}

输出结果是2 1 0,逆序输出了.

[图片上传失败...(image-a83c5c-1618579540526)]

分析一下,我们用的是表头法插入,第一次插入的是0,第二次插入的1,在0之前,第三次插入的是2,在1之前

表尾插入法就很简单了

if(pMove->next==Null)
    pMove->next=newNode;

当然要先找到表尾节点,我们通过while循环实现一下,不一定要使用

void insertNodeFromTail(struct Node* headNode,int data)
{
    struct Node* pMove=headNode;//创建一个用于移动的结构体指针,指向头节点
    while(pMove->next!=NULL)
    {
        pMove=pMove->next;//不断的判断是否是表尾,并且移动到下一个节点,循环结束后就移动到表尾了
    }  
    struct Node* newNode=createNode(data);
    pMove->next=newNode;
}
调试代码在main函数中同样的增加如下
struct Node* list=createHead();
    for(int i=0;i<3;i++)
    {
        insertNodeFromtail(list, i);

    }
    printList(list);
这样输出的就是 0 1 2了

3.4指定位置删除(删除确定的节点)

image-20210202191617812
知道删除节点左边的节点要删除这个节点
image-20210202191814709
就是要把posLeftNode链接到后面这个节点
posLeftNode->next=posNode->next;
free(posNode);//然后释放内存
posNode=NULL;//删除的节点置空
封装一下函数
void deleteNodeByData(struct Node* headNode, int posData)
{
    struct Node* posLeftNode=headNode;
    struct Node* posNode=headNode->next;//删除指针相邻的节点,一前一后
    while(posNode!=NULL&&posNode->data!=posData) //当要删除的节点不等于空节点,并且数据不是要删除的数据,节点向后移动
    {
        posLeftNode=posNode;
        posNode=posLeftNode->next;//这两个节点并排向前移动
    }
    if(posNode==NULL)
        return; //没有找到
    else
    {
        posLeftNode->next=posNode->next;
        free(posNode);//然后释放内存
        posNode=NULL;//删除的节点置空
       
    }
    
}
测试代码
int main()
{

    struct Node* list=createHead();
    for(int i=0;i<3;i++)
    {
        insertNodeFromHead(list, i);

    }
    deleteNodeByData(list,1);
    printList(list);


    while(1)
    {
        makeMenu();
        keyDown();
        system("pause");
        system("cls");
        
    }
    return 0;
}

调试发现,输出2,0

现在我们实现了一个结构体里面有一个指针变量+一个整型变量的链表操作

接下来我们完善图书的结构体

struct bookInfo
{
    char bookName[20];//书名
    // char version[20];
    float price;//价格
    int num;//这本书的数量
};

然后我们修改一下上面的代码,将struct Node中的成员 替换为

struct Node
{
    struct bookInfo data; //放入图书结构体变量
    struct Node* next;
};

然后把所有的int data替换为 struct bookInfo data

打印printList()代码替换为

void printList(struct Node* headNode)
{
    struct Node* pMove=headNode->next; //打印表头的下一个链表的内容,我们用一个指针保存
    printf("书名\t价格\t数量\n");
    while(pMove)//循环指针指到NULL末尾 等价于 while(pMove!=NULL)
    {
        printf("%s\t%.1f\t%d\n",pMove->data.bookName,pMove->data.price,pMove->data.num); //成员像剥洋葱一样依次展开打印
        pMove=pMove->next;
    }
}

删除的代码页需要修改,比如我们通过书名删除

void deleteNodeByData(struct Node* headNode, char *deletedbookName)
{
    struct Node* posLeftNode=headNode;
    struct Node* posNode=headNode->next;//删除指针相邻的节点,一前一后
    //使用字符串比较函数,进的include
    while(posNode!=NULL&&strcmp(posNode->data.bookName,deletedbookName)) //当要删除的节点不等于空节点,并且数据不是要删除的数据,节点向后移动
    {
        posLeftNode=posNode;
        posNode=posLeftNode->next;//这两个节点并排向前移动
    }
    
    if(posNode==NULL)
        return; //没有找到
    else
    {
        posLeftNode->next=posNode->next;
        free(posNode);//然后释放内存
        posNode=NULL;//删除的节点置空 
    }
    
}

功能差不多做出来了,现在要开始做交互功能,把刚刚的交互代码新增一些东西

void keyDown() //判断用户输入的子菜单数字去判断
{
    int userKey=0; //默认初始值为0退出
    struct bookInfo tempBook;//产生一个临时的变量储存书籍信息
    scanf("%d",&userKey);//用scanf获得整数 一般是没有什么坑的
    //思考一下,判断数字是0~7的数用什么处理
    switch(userKey)
    {
        case 0://退出模块,做一个人机界面 提醒
            printf("【退出】\n");
            break;
        case 1://复制粘贴的操作,快速处理一下
             printf("【登记】\n");
            printf("输入书籍的信息(name,price,num):");
            scanf("%s%f%d",tempBook.bookName,&tempBook.price,&tempBook.num);
            break;
        case 2:
             printf("【浏览】\n");
            break;
        case 3:
             printf("【借阅】\n");
            break;
        case 4:
             printf("【归还】\n");
            break;
        case 5:
             printf("【排序】\n");
            break;
        case 6:
             printf("【删除】\n");
            break;
        case 7:
             printf("【查找】\n");
            break;
        default:
            printf("【erro】\n");
            
    }
}

现在我们对链表做操作,把链表作为全局链表方便一点操作,然后在main函数初始化

全局变量加一句

struct Node
{
    struct bookInfo data;
    struct Node* next; //有表头的链表
};
struct Node* list=NULL;

main函数里面初始化

int main()
{
//struct Node* list=createHead();
//注释掉,不然list是局部变量,没有使用全局变量

    list=createHead();
}

接着完善 case 1中的登记 也就是插入链表的操作

void keyDown() //判断用户输入的子菜单数字去判断
{
    int userKey=0; //默认初始值为0退出
    struct bookInfo tempBook;//产生一个临时的变量储存书籍信息
    scanf("%d",&userKey);//用scanf获得整数 一般是没有什么坑的
    //思考一下,判断数字是0~7的数用什么处理
    switch(userKey)
    {
        case 0://退出模块,做一个人机界面 提醒
            printf("【退出】\n");
            break;
        case 1://复制粘贴的操作,快速处理一下
             printf("【登记】\n");
            printf("输入书籍的信息(name,price,num):");
            scanf("%s%f%d",tempBook.bookName,&tempBook.price,&tempBook.num);
            insertNodeFromHead(list,tempBook); //登记
            break;
        case 2:
             printf("【浏览】\n");
            //浏览就是打印
            printList(list);
            break;
        case 3:
             printf("【借阅】\n");
            break;
        case 4:
             printf("【归还】\n");
            break;
        case 5:
             printf("【排序】\n");
            break;
        case 6:
             printf("【删除】\n");
            break;
        case 7:
             printf("【查找】\n");
            break;
        default:
            printf("【erro】\n");
            
    }
}
这段代码就在main函数之前调用,因为前面有不少前置功能函数
调试一下,尝试新增一些书籍,和浏览。
记得scanf中我们用%s%f%d,所以是空格间隔这些东西

4.进阶一下,文件操作( 方便调试)

我们每次调试运行,都要手动初始化一下list的数据,增加书本信息

实际上文件操作,就是对list进行操作, 用文件的方式给它list读取 初始化

4.1文件存操作

void saveInfoTofile(const char fileName,struct Node *headNode)

把文件名为fileName的文件,存到headNode链表中

void readInfoFromFile(const char fileName,struct Node *headNode)

把headNode链表中的信息,写入fileName

两个函数同时做

void saveInfoToFile(const char *fileName,struct Node* headNode)
    
{
    FILE *fp=fopen(fileName,"w");//以写的方式
    struct Node* pMove=headNode->next;//获得一个pMove节点,为第二个节点
    while(pMove!=NULL)
    {
        fprintf(fp,"%s\t%.1f\t%d\n",pMove->data.bookName,pMove->data.price,pMove->data.num);
        pMove=pMove->next;
        //把数据输出到文件 fprintf函数,用对应的格式
        
        /*struct Node
{
    struct bookInfo data;
    struct Node* next; //有表头的链表
};
*/
        //容器中放入的是 结构体指针,与书籍结构体变量,所以通过链表这个结构体找到 数据这个结构体变量,在通过.运算符 访问具体信息
    }
   
    
    fclose(fp);
    
}

4.2文件读操作

void readInfoFromFile(const char *fileName,struct Node* headNode)
{
     FILE *fp=fopen(fileName,"r");//以读的方式
     if(fp==NULL)
    {
       //不存在,创建文件
        fp=fopen(fileName,"w+");//以创建的方式打开
    }
    
    fclose(fp);
    
    
}

读的话,是用 fscanf函数,当然需要读到一个变量,我们新增一个临时变量,接受这些值

void readInfoFromFile(const char *fileName,struct Node* headNode)
{
     FILE *fp=fopen(fileName,"r");//以读的方式
     if(fp==NULL)
    {
       //不存在,创建文件
        fp=fopen(fileName,"w+");//以创建的方式打开
    }
    struct bookInfo tempData;
    while(fscanf(fp,"%s\t%f\t%d\n",tempData.bookName,&tempData.price,&tempData.num)!=EOF)
        //读到这个变量里面去,不做格式控制,当他读到的时候,插入到链表Node里面
    {
        insertNodeFromHead(list,tempData);
    }
    
    fclose(fp);
    
    
}
    
    

调用一下

main函数中

int main()
{

    list=createHead();
    readInfoFromFile("bookinfo.txt",list); //主程序运行就读取文件到list
    


    while(1)
    {
        makeMenu();
        keyDown();
        system("pause");
        system("cls");
        
    }
    return 0;
}

当我们修改了东西的时候,往文件里面写写入

登记,就是插入链表

case 1:

具体的说就是 case 1里面增加 saveInfotoFile("bookinfo.txt",list);

void keyDown() //判断用户输入的子菜单数字去判断
{
    int userKey=0; //默认初始值为0退出
    struct bookInfo tempBook;//产生一个临时的变量储存书籍信息
    scanf("%d",&userKey);//用scanf获得整数 一般是没有什么坑的
    //思考一下,判断数字是0~7的数用什么处理
    switch(userKey)
    {
        case 0://退出模块,做一个人机界面 提醒
            printf("【退出】\n");
            break;
        case 1://复制粘贴的操作,快速处理一下
             printf("【登记】\n");
            printf("输入书籍的信息(name,price,num):");
            scanf("%s%f%d",tempBook.bookName,&tempBook.price,&tempBook.num);
            insertNodeFromHead(list,tempBook);
            saveInfoToFile("bookinfo.txt",list);
            break;
        case 2:
             printf("【浏览】\n");
             printList(list);
            break;
        case 3:
             printf("【借阅】\n");
            break;
        case 4:
             printf("【归还】\n");
            break;
        case 5:
             printf("【排序】\n");
            break;
        case 6:
             printf("【删除】\n");
            break;
        case 7:
             printf("【查找】\n");
            break;
        default:
            printf("【erro】\n");
            
    }
}

测试一下,发现写了一个bookinfo.txt文件(工程目录下)

那么case 2中浏览书籍的代码更改就很容易了

先读文件,然后在打印

浏览

case 2

case 2:
             printf("【浏览】\n");
             printList(list);
            break;

attention:

1.链表节点Node与图书结构体变量的包含关系

2.文件的读过程,用fscanf()函数往临时图书结构体变量里面TempData,文件的所有内容,都要通过结构体变量的方式,循环读取到末尾,保存一个TempData就调用一次 insertNodeFromHead插入函数,组成链表

3.文件写过程,用fprintf()函数,对链表Node结构体的指针pMove进行是否为空的操作,而不是对fp进行是否为空的操作,通过链表Node结构体指针pMove访问内部的 图书结构体变量这个成员,再访问图书结构体变量中的自己的成员

(套娃pMove->data.bookName)

4.注意什么时候是对list与headNode的区别与联系,由于list是全局的headNode,每次需要更新整个链表,使用list,对全局影响。而在功能函数的封装中,插入单个节点,删除单个节点的函数中使用headNode头指针进行局部操作。


排序

case 5

//冒泡排序,相邻的两个元素相互比较
void bubbleSortList(struct Node* headNode)
{
    for(struct Node* p=headNode->next;p!=NULL;p=p->next)
    {
        for(struct Node* q=headNode->next;q->next!=NULL;q=q->next) //
 
        {
            if(q->data.price>(q->next->data.price)) //如果q节点中成员data结构体变量中成员price,大于下一个节点
            {
                //交换
                //需要临时变量,这个临时变量是一个 strcut bookInfo结构体变量
                struct bookInfo tempData=q->data;
                q->data=(q->next->data);
                q->next->data=tempData;
            }
        }
    }
    printList(headNode);
}

注意第二次for循环中的 循环终止条件是 q->next!=NULL,与外层循环的p!=NULL不一样

调用一下就可以调试了

        case 5:
             printf("【排序】\n");
             bubbleSortList(list);

删除

我们是通过书籍名称删除

void deleteNodeByData()函数修改为 void deleteNodeByName()

case 6 直接调用删除函数

 case 6:
             printf("【删除】\n");
             printf("请输入删除的书名\n");
             scanf("%s",tempBook.bookName); //前面定义了一个tempBook临时结构体变量
             deleteNodeByName(list,tempBook.bookName);
//涉及了数据的修改,要同步到文件
            saveInfoToFile("bookinfo.txt",list);

查找,通过姓名

查找等效于链表的查找,找到节点后返回

struct Node* searchByName(struct Node* headNode, char* optbookName)
{
    struct Node* posNode=headNode->next;
    while(posNode!=NULL&&strcmp(posNode->data.bookName,optbookName))
    {
        posNode=posNode->next;
    }
    return posNode;
}

在case 7里面调用一下函数,返回的这个指针抛出一个人机交互,找到与没找到。
keydown()函数新声明一个
result指针
struct Node* result=NULL;

case 7:
             printf("【查找】\n");
              printf("请输入要查找的书名\n");
              scanf("%s",tempBook.bookName); 
              result=searchByName(list,tempBook.bookName);
              if(result==NULL)
              {
                  printf("没有找到");
              }
              else
              {
                  printf("书名\t价格\t数量\n");
                  printf("%s\t%.1f\t%d\n",result->data.bookName,result->data.price,result->data.num);
              }

            break;

并且在 keyDown()函数初始化开始新增一个临时指针变量

并且完善一下退出机制,尽管是while(1)死循环,但是我们可以在case 0里面,输入 exit(0)退出整个程序

更新过的keyDown()交互函数如下

void keyDown() //判断用户输入的子菜单数字去判断
{
    int userKey=0; //默认初始值为0退出
    struct bookInfo tempBook;//产生一个临时的变量储存书籍信息
     struct Node* result=NULL;
    scanf("%d",&userKey);//用scanf获得整数 一般是没有什么坑的
    //思考一下,判断数字是0~7的数用什么处理
    switch(userKey)
    {
        case 0://退出模块,做一个人机界面 提醒
            printf("【退出】\n");
            exit(0);
            break;
        case 1://复制粘贴的操作,快速处理一下
            
            printf("【登记】\n");
            printf("输入书籍的信息(name,price,num):");
            scanf("%s%f%d",tempBook.bookName,&tempBook.price,&tempBook.num);
            insertNodeFromHead(list,tempBook);
            saveInfoToFile("bookinfo.txt",list);
            break;
        case 2:
             printf("【浏览】\n");
             printList(list);
            break;
        case 3:
             printf("【借阅】\n");
            break;
        case 4:
             printf("【归还】\n");
            break;
        case 5:
             printf("【排序】\n");
             bubbleSortList(list);
            break;
        case 6:
             printf("【删除】\n");
             printf("请输入删除的书名\n");
             scanf("%s",tempBook.bookName); //前面定义了一个tempBook临时结构体变量
             deleteNodeByName(list,tempBook.bookName);
//涉及了数据的修改,要同步到文件
            saveInfoToFile("bookinfo.txt",list);
             
            break;
        case 7:
             printf("【查找】\n");
              printf("请输入要查找的书名\n");
              scanf("%s",tempBook.bookName); 
              result=searchByName(list,tempBook.bookName);
              if(result==NULL)
              {
                  printf("没有找到");
              }
              else
              {
                  printf("书名\t价格\t数量\n");
                  printf("%s\t%.1f\t%d\n",result->data.bookName,result->data.price,result->data.num);
              }

            break;
        default:
            printf("【erro】\n");
            
    }
}

5.剩下的工作,借阅和归还

这里的功能与其他部分增删查改不一样

①书籍存在,可以借阅,总数量-1,借阅成功,借阅前查询是否有判断,有这类书还要进行是否有库存的判断
②归还,当前数量+1

借阅case 3

case 3:
    printf("【借阅】\n");
    printf("请输入借阅的书名\n");
    scanf("%s",tempBook.bookName);
    result=searchByName(list,tempBook.bookName);
    if(result==NULL)
    {
        printf("没有相关书籍,没法借阅\n");

    }
    else
    {
        if(result->data.num>0)
       
        {
            result->data.num--;
            printf("借阅成功!\n");
            //借阅成功了,库存减小,这里要更新一下list
            saveInfoToFile("bookinfo.txt",list);
        }
        else
        {
            printf("借阅失败,没有库存!\n");
        }
    }
    break;

归还case 4

归还很简单,复用一下代码,并且可以简化一下存在与否的查找

不需要判断库存是否大于0,直接++,更新list即可

case 4:
    printf("【归还】\n");
    printf("请输入归还的书名\n");
    scanf("%s",tempBook.bookName);
    result=searchByName(list,tempBook.bookName);
    if(result==NULL)
    {
        printf("该书来源非法\n");

    }
    else
    {
        result->data.num++;
        printf("归还成功!\n");
       //归还成功了,这里要更新一下list
        saveInfoToFile("bookinfo.txt",list);
    
    }
        
    
    break;

结束

总体代码如下,仅适合调试不成功的对比,不要直接复制

#include
#include
#include
void makeMenu()
{
    printf("-------------------------------\n");
    printf("gdut图书馆里系统\n");
    printf("\t0.退出系统\n");
    printf("\t1.登记书籍\n");
    printf("\t2.浏览书籍\n");
    printf("\t3.借阅书籍\n");
    printf("\t4.归还书籍\n");
    printf("\t5.书籍排序\n");
    printf("\t6.删除书籍\n");
    printf("\t7.查找书籍\n");
    printf("请输入(0~7)\n");
    printf("-------------------------------\n");
}

struct bookInfo
{
    char bookName[20];//书名
    // char version[20];
    float price;//价格
    int num;//这本书的数量
};
struct Node
{
    struct bookInfo data;
    struct Node* next; //有表头的链表
};
struct Node* list=NULL;

//创建表头: 表头就是一个结构体变量
struct Node* createHead()
{
    //动态内存申请
    struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
    //变量的基本规则,使用前必须初始化
    headNode->next=NULL;//表头可以不存数据
    return headNode;
}
//创建节点: 为插入节点做准备
//把用户的数据作为参数,传入 变为结构体变量
struct Node* createNode(struct bookInfo data)
{
    struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
    newNode->data=data;  //节点的数据,用来做初始化
    newNode->next=NULL;
    return newNode;
}
//打印链表
void printList(struct Node* headNode)
{
    struct Node* pMove=headNode->next; //打印表头的下一个链表的内容,我们用一个指针保存
    printf("书名\t价格\t数量\n");
    while(pMove)//循环指针指到NULL末尾 等价于 while(pMove!=NULL)
    {
        printf("%s\t%.1f\t%d\n",pMove->data.bookName,pMove->data.price,pMove->data.num);
        pMove=pMove->next;
    }
}
void insertNodeFromHead(struct Node* headNode, struct bookInfo data)
{
    struct Node* newNode=createNode(data); //用户输入的data传入createNode函数,返回一个结构体变量指针,创建一个newNode
    newNode->next=headNode->next;
    headNode->next=newNode;
}
void insertNodeFromTail(struct Node* headNode,struct bookInfo data)
{
    struct Node* pMove=headNode;//创建一个用于移动的结构体指针,指向头节点
    while(pMove->next!=NULL)
    {
        pMove=pMove->next;//不断的判断是否是表尾,并且移动到下一个节点,循环结束后就移动到表尾了
    }  
    struct Node* newNode=createNode(data);
    pMove->next=newNode;
}
void deleteNodeByName(struct Node* headNode, char *deletedbookName)
{
    struct Node* posLeftNode=headNode;
    struct Node* posNode=headNode->next;//删除指针相邻的节点,一前一后
    //使用字符串比较函数,进的include
    while(posNode!=NULL&&strcmp(posNode->data.bookName,deletedbookName)) //当要删除的节点不等于空节点,并且数据不是要删除的数据,节点向后移动
    {
        posLeftNode=posNode;
        posNode=posLeftNode->next;//这两个节点并排向前移动
    }
    
    if(posNode==NULL)
        return; //没有找到
    else
    {
        printf("删除成功!\n");
        posLeftNode->next=posNode->next;
        free(posNode);//然后释放内存
        posNode=NULL;//删除的节点置空 
    }
    
}
void saveInfoToFile(const char *fileName,struct Node* headNode)
    
{
    FILE *fp=fopen(fileName,"w");//以写的方式
    struct Node* pMove=headNode->next;//获得一个pMove节点,为第二个节点
    while(pMove!=NULL)
    {
        fprintf(fp,"%s\t%.1f\t%d\n",pMove->data.bookName,pMove->data.price,pMove->data.num);
        pMove=pMove->next;
        //把数据输出到文件 fprintf函数,用对应的格式
        
        /*struct Node
{
    struct bookInfo data;
    struct Node* next; //有表头的链表
};
*/
        //容器中放入的是 结构体指针,与书籍结构体变量,所以通过链表这个结构体找到 数据这个结构体变量,在通过.运算符 访问具体信息
    }
   
    
    fclose(fp);
    
}

void readInfoFromFile(const char *fileName,struct Node* headNode)
{
     FILE *fp=fopen(fileName,"r");//以读的方式
     if(fp==NULL)
    {
       //不存在,创建文件
        fp=fopen(fileName,"w+");//以创建的方式打开
    }
    struct bookInfo tempData;
    while(fscanf(fp,"%s\t%f\t%d\n",tempData.bookName,&tempData.price,&tempData.num)!=EOF)
        //读到这个变量里面去,不做格式控制,当他读到的时候,插入到链表Node里面
    {
        insertNodeFromHead(list,tempData);
    }
    
    fclose(fp);
    
    
}
//冒泡排序,相邻的两个元素相互比较
void bubbleSortList(struct Node* headNode)
{
    for(struct Node* p=headNode->next;p!=NULL;p=p->next)
    {
        for(struct Node* q=headNode->next;q->next!=NULL;q=q->next)
        {
            if(q->data.price>(q->next->data.price)) //如果q节点中成员data结构体变量中成员price,大于下一个节点
            {
                //交换
                //需要临时变量,这个临时变量是一个 strcut bookInfo结构体变量
                struct bookInfo tempData=q->data;
                q->data=(q->next->data);
                q->next->data=tempData;
            }
        }
    }
    printList(headNode);
}
struct Node* searchByName(struct Node* headNode, char* optbookName)
{
    struct Node* posNode=headNode->next;
    while(posNode!=NULL&&strcmp(posNode->data.bookName,optbookName))
    {
        posNode=posNode->next;
    }
    return posNode;
}
    
void keyDown() //判断用户输入的子菜单数字去判断
{
    int userKey=0; //默认初始值为0退出
    struct bookInfo tempBook;//产生一个临时的变量储存书籍信息
     struct Node* result=NULL;
    scanf("%d",&userKey);//用scanf获得整数 一般是没有什么坑的
    //思考一下,判断数字是0~7的数用什么处理
    switch(userKey)
    {
        case 0://退出模块,做一个人机界面 提醒
            printf("【退出】\n");
            exit(0);
            break;
        case 1://复制粘贴的操作,快速处理一下
            
            printf("【登记】\n");
            printf("输入书籍的信息(name,price,num):");
            scanf("%s%f%d",tempBook.bookName,&tempBook.price,&tempBook.num);
            insertNodeFromHead(list,tempBook);
            saveInfoToFile("bookinfo.txt",list);
            break;
        case 2:
             printf("【浏览】\n");
             printList(list);
            break;
        case 3:
    
            printf("【借阅】\n");
            printf("请输入借阅的书名\n");
            scanf("%s",tempBook.bookName);
            result=searchByName(list,tempBook.bookName);
            if(result==NULL)
            {
                printf("没有相关书籍,没法借阅\n");
            }
            else
            {
                if(result->data.num>0)
                {
                    result->data.num--;
                    printf("借阅成功!\n");//借阅成功了,库存减小,这里要更新一下list
                    saveInfoToFile("bookinfo.txt",list);
                }
                else
                {
                    printf("借阅失败,没有库存!\n");
                }
            }
    break;
        case 4:
             printf("【归还】\n");
             printf("请输入归还的书名\n");
             scanf("%s",tempBook.bookName);
             result=searchByName(list,tempBook.bookName);
             if(result==NULL)
             {
                 printf("该书来源非法\n");

             }
             else
             {
                 result->data.num++;
                 printf("归还成功!\n");//归还成功了,这里要更新一下list
                 saveInfoToFile("bookinfo.txt",list);
             }
        
    
    break;
        case 5:
             printf("【排序】\n");
             bubbleSortList(list);
            break;
        case 6:
             printf("【删除】\n");
             printf("请输入删除的书名\n");
             scanf("%s",tempBook.bookName); //前面定义了一个tempBook临时结构体变量
             deleteNodeByName(list,tempBook.bookName);
//涉及了数据的修改,要同步到文件
            saveInfoToFile("bookinfo.txt",list);
             
            break;
        case 7:
             printf("【查找】\n");
              printf("请输入要查找的书名\n");
              scanf("%s",tempBook.bookName); 
             
              result=searchByName(list,tempBook.bookName);
              if(result==NULL)
              {
                  printf("没有找到");
              }
              else
              {
                  printf("书名\t价格\t数量\n");
                  printf("%s\t%.1f\t%d\n",result->data.bookName,result->data.price,result->data.num);
              }

            break;
        default:
            printf("【erro】\n");
            
    }
}

int main()
{

    list=createHead();
    readInfoFromFile("bookinfo.txt",list);
    
    while(1)
    {
        makeMenu();
        keyDown();
        system("pause");
        system("cls");
        
    }

    return 0;
}

你可能感兴趣的:(图书馆信息管理系统)