图书馆信息管理系统

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

预备知识:

(1)指针如何变成变量

#include <stdio.h>
#include<stdlib.h>


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<string.h>
    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<stdio.h>
#include<stdlib.h>
#include<string.h>
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<string.h>
    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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容