图书管理系统的设计与实现!增删改查
预备知识:
(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;
}
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程序用什么容器装数据(数组,或者链表)
数组是连续的内存,链表是离散的内存
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插入,只需要一种插入方式(表头法插入)
第一个节点叫headNode,第二个节点就叫headnode->next
新插入的节点newNode,第一步连到headNode->next
第二部headNode连到newNode
//代码实现
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指定位置删除(删除确定的节点)
知道删除节点左边的节点要删除这个节点
就是要把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;
}