圖書管理系統(tǒng)的設(shè)計與實現(xiàn)!增刪改查
預(yù)備知識:
(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 動態(tài)內(nèi)存申請
p=(int *)malloc(sizeof(int)); //malloc()函數(shù)需要 stdlib.h頭文件
*p=10033;
printf("%d\n",*p);
}
(2)什么是結(jié)構(gòu)體
結(jié)構(gòu)體是新的類型,將一些自定義的數(shù)據(jù)類型組合**打包**起來,放在堆棧中
int A=1;
float B=1.1;
char name[10];
//放入結(jié)構(gòu)體中,這三個變量塊就放在一起打包了
struct data
{
int A=1;
float B=1.1;
char name[10];
};
struct data c;
//通過變量訪問 c.A
//通過指針訪問 struct *data pData=&c
//pData->c
(3)什么是鏈表
鏈表是用指針把一些結(jié)構(gòu)體變量鏈接起來(結(jié)構(gòu)體變量c1的成員中,會放入一個結(jié)構(gòu)體變量指針 指向下一個結(jié)構(gòu)體變量c2)
鏈表就是由結(jié)構(gòu)體變量組成的線性數(shù)據(jù)結(jié)構(gòu)
項目開始
1.寫界面--->菜單---->子模塊總覽
void makeMenu()
{
printf("-------------------------------\n");
printf("gdut圖書館里系統(tǒng)\n");
printf("\t0.退出系統(tǒng)\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() //判斷用戶輸入的子菜單數(shù)字去判斷
{
int userKey=0; //默認(rèn)初始值為0退出
scanf("%d",&userKey);//用scanf獲得整數(shù) 一般是沒有什么坑的
//思考一下,判斷數(shù)字是0~7的數(shù)用什么處理
switch(userKey)
{
case 0://退出模塊,做一個人機界面 提醒
printf("【退出】\n");
break;
case 1://復(fù)制粘貼的操作,快速處理一下
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函數(shù)里面做while(1)循環(huán)處理
int main()
{
while(1)
{
makeMenu();
keyDown();
system("pause"); //有的編譯系統(tǒng)會直接結(jié)束,讓程序等待一下
system("cls");//防止閃屏,在這里我們還可以將上一部分的內(nèi)容清楚
}
return 0;
}
3.設(shè)計數(shù)據(jù)(先設(shè)計容器,再處理存取刪改數(shù)據(jù))
3.1程序用什么容器裝數(shù)據(jù)(數(shù)組,或者鏈表)
數(shù)組是連續(xù)的內(nèi)存,鏈表是離散的內(nèi)存

3.2數(shù)據(jù)的結(jié)構(gòu)(圖書的信息)
struct Node
{
int data;
struct Node* next; //有表頭的鏈表
};
//創(chuàng)建表頭: 表頭就是一個結(jié)構(gòu)體變量
struct Node* createHead()
{
//動態(tài)內(nèi)存申請
struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
//變量的基本規(guī)則,使用前必須初始化
headNode->next=NULL;//表頭可以不存數(shù)據(jù)
return headNode;
}
//創(chuàng)建節(jié)點: 為插入節(jié)點做準(zhǔn)備
//把用戶的數(shù)據(jù)作為參數(shù),傳入 變?yōu)榻Y(jié)構(gòu)體變量
struct Node* createNode(int data)
{
struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=data; //節(jié)點的數(shù)據(jù),用來做初始化
newNode->next=NULL;
return newNode;
}
//打印鏈表
void printList(struct Node* headNode)
{
struct Node* pMove=headNode->next; //打印表頭的下一個鏈表的內(nèi)容,我們用一個指針保存
while(pMove)//循環(huán)指針指到NULL末尾 等價于 while(pMove!=NULL)
{
printf("%d\t",pMove->data);
pMove=pMove->next;
}
}
3.3插入,只需要一種插入方式(表頭法插入)

第一個節(jié)點叫headNode,第二個節(jié)點就叫headnode->next
新插入的節(jié)點newNode,第一步連到headNode->next
第二部headNode連到newNode

//代碼實現(xiàn)
newNode->next=headNode->next;
headNode->next=newNode;
測試一下
void insertNodeFromHead(struct Node* headNode, int data)
{
struct Node* newNode=createNode(data); //用戶輸入的data傳入createNode函數(shù),返回一個結(jié)構(gòu)體變量指針,創(chuàng)建一個newNode
//必須先鏈接,后斷開
newNode->next=headNode->next;
headNode->next=newNode;
}
把3.2中的代碼復(fù)制到main函數(shù)之前(結(jié)構(gòu)體聲明,創(chuàng)建頭節(jié)點,創(chuàng)建新節(jié)點,輸出節(jié)點成員變量)
然后main函數(shù)增加一下代碼,我們試著調(diào)試一下
struct Node* list=createHead();
for(int i=0;i<3;i++)
{
insertNodeFromHead(list, i);
}
printList(list);
完整main函數(shù)如下
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;
}
輸出結(jié)果是2 1 0,逆序輸出了.
[圖片上傳失敗...(image-a83c5c-1618579540526)]
分析一下,我們用的是表頭法插入,第一次插入的是0,第二次插入的1,在0之前,第三次插入的是2,在1之前
表尾插入法就很簡單了
if(pMove->next==Null)
pMove->next=newNode;
當(dāng)然要先找到表尾節(jié)點,我們通過while循環(huán)實現(xiàn)一下,不一定要使用
void insertNodeFromTail(struct Node* headNode,int data)
{
struct Node* pMove=headNode;//創(chuàng)建一個用于移動的結(jié)構(gòu)體指針,指向頭節(jié)點
while(pMove->next!=NULL)
{
pMove=pMove->next;//不斷的判斷是否是表尾,并且移動到下一個節(jié)點,循環(huán)結(jié)束后就移動到表尾了
}
struct Node* newNode=createNode(data);
pMove->next=newNode;
}
調(diào)試代碼在main函數(shù)中同樣的增加如下
struct Node* list=createHead();
for(int i=0;i<3;i++)
{
insertNodeFromtail(list, i);
}
printList(list);
這樣輸出的就是 0 1 2了
3.4指定位置刪除(刪除確定的節(jié)點)

知道刪除節(jié)點左邊的節(jié)點要刪除這個節(jié)點

就是要把posLeftNode鏈接到后面這個節(jié)點
posLeftNode->next=posNode->next;
free(posNode);//然后釋放內(nèi)存
posNode=NULL;//刪除的節(jié)點置空
封裝一下函數(shù)
void deleteNodeByData(struct Node* headNode, int posData)
{
struct Node* posLeftNode=headNode;
struct Node* posNode=headNode->next;//刪除指針相鄰的節(jié)點,一前一后
while(posNode!=NULL&&posNode->data!=posData) //當(dāng)要刪除的節(jié)點不等于空節(jié)點,并且數(shù)據(jù)不是要刪除的數(shù)據(jù),節(jié)點向后移動
{
posLeftNode=posNode;
posNode=posLeftNode->next;//這兩個節(jié)點并排向前移動
}
if(posNode==NULL)
return; //沒有找到
else
{
posLeftNode->next=posNode->next;
free(posNode);//然后釋放內(nèi)存
posNode=NULL;//刪除的節(jié)點置空
}
}
測試代碼
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;
}
調(diào)試發(fā)現(xiàn),輸出2,0
現(xiàn)在我們實現(xiàn)了一個結(jié)構(gòu)體里面有一個指針變量+一個整型變量的鏈表操作
接下來我們完善圖書的結(jié)構(gòu)體
struct bookInfo
{
char bookName[20];//書名
// char version[20];
float price;//價格
int num;//這本書的數(shù)量
};
然后我們修改一下上面的代碼,將struct Node中的成員 替換為
struct Node
{
struct bookInfo data; //放入圖書結(jié)構(gòu)體變量
struct Node* next;
};
然后把所有的int data替換為 struct bookInfo data
打印printList()代碼替換為
void printList(struct Node* headNode)
{
struct Node* pMove=headNode->next; //打印表頭的下一個鏈表的內(nèi)容,我們用一個指針保存
printf("書名\t價格\t數(shù)量\n");
while(pMove)//循環(huán)指針指到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;//刪除指針相鄰的節(jié)點,一前一后
//使用字符串比較函數(shù),進的include<string.h>
while(posNode!=NULL&&strcmp(posNode->data.bookName,deletedbookName)) //當(dāng)要刪除的節(jié)點不等于空節(jié)點,并且數(shù)據(jù)不是要刪除的數(shù)據(jù),節(jié)點向后移動
{
posLeftNode=posNode;
posNode=posLeftNode->next;//這兩個節(jié)點并排向前移動
}
if(posNode==NULL)
return; //沒有找到
else
{
posLeftNode->next=posNode->next;
free(posNode);//然后釋放內(nèi)存
posNode=NULL;//刪除的節(jié)點置空
}
}
功能差不多做出來了,現(xiàn)在要開始做交互功能,把剛剛的交互代碼新增一些東西
void keyDown() //判斷用戶輸入的子菜單數(shù)字去判斷
{
int userKey=0; //默認(rèn)初始值為0退出
struct bookInfo tempBook;//產(chǎn)生一個臨時的變量儲存書籍信息
scanf("%d",&userKey);//用scanf獲得整數(shù) 一般是沒有什么坑的
//思考一下,判斷數(shù)字是0~7的數(shù)用什么處理
switch(userKey)
{
case 0://退出模塊,做一個人機界面 提醒
printf("【退出】\n");
break;
case 1://復(fù)制粘貼的操作,快速處理一下
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");
}
}
現(xiàn)在我們對鏈表做操作,把鏈表作為全局鏈表方便一點操作,然后在main函數(shù)初始化
全局變量加一句
struct Node
{
struct bookInfo data;
struct Node* next; //有表頭的鏈表
};
struct Node* list=NULL;
main函數(shù)里面初始化
int main()
{
//struct Node* list=createHead();
//注釋掉,不然list是局部變量,沒有使用全局變量
list=createHead();
}
接著完善 case 1中的登記 也就是插入鏈表的操作
void keyDown() //判斷用戶輸入的子菜單數(shù)字去判斷
{
int userKey=0; //默認(rèn)初始值為0退出
struct bookInfo tempBook;//產(chǎn)生一個臨時的變量儲存書籍信息
scanf("%d",&userKey);//用scanf獲得整數(shù) 一般是沒有什么坑的
//思考一下,判斷數(shù)字是0~7的數(shù)用什么處理
switch(userKey)
{
case 0://退出模塊,做一個人機界面 提醒
printf("【退出】\n");
break;
case 1://復(fù)制粘貼的操作,快速處理一下
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函數(shù)之前調(diào)用,因為前面有不少前置功能函數(shù)
調(diào)試一下,嘗試新增一些書籍,和瀏覽。
記得scanf中我們用%s%f%d,所以是空格間隔這些東西
4.進階一下,文件操作( 方便調(diào)試)
我們每次調(diào)試運行,都要手動初始化一下list的數(shù)據(jù),增加書本信息
實際上文件操作,就是對list進行操作, 用文件的方式給它list讀取 初始化
4.1文件存操作
void saveInfoTofile(const char fileName,struct Node *headNode)
把文件名為fileName的文件,存到headNode鏈表中
void readInfoFromFile(const char fileName,struct Node *headNode)
把headNode鏈表中的信息,寫入fileName
兩個函數(shù)同時做
void saveInfoToFile(const char *fileName,struct Node* headNode)
{
FILE *fp=fopen(fileName,"w");//以寫的方式
struct Node* pMove=headNode->next;//獲得一個pMove節(jié)點,為第二個節(jié)點
while(pMove!=NULL)
{
fprintf(fp,"%s\t%.1f\t%d\n",pMove->data.bookName,pMove->data.price,pMove->data.num);
pMove=pMove->next;
//把數(shù)據(jù)輸出到文件 fprintf函數(shù),用對應(yīng)的格式
/*struct Node
{
struct bookInfo data;
struct Node* next; //有表頭的鏈表
};
*/
//容器中放入的是 結(jié)構(gòu)體指針,與書籍結(jié)構(gòu)體變量,所以通過鏈表這個結(jié)構(gòu)體找到 數(shù)據(jù)這個結(jié)構(gòu)體變量,在通過.運算符 訪問具體信息
}
fclose(fp);
}
4.2文件讀操作
void readInfoFromFile(const char *fileName,struct Node* headNode)
{
FILE *fp=fopen(fileName,"r");//以讀的方式
if(fp==NULL)
{
//不存在,創(chuàng)建文件
fp=fopen(fileName,"w+");//以創(chuàng)建的方式打開
}
fclose(fp);
}
讀的話,是用 fscanf函數(shù),當(dāng)然需要讀到一個變量,我們新增一個臨時變量,接受這些值
void readInfoFromFile(const char *fileName,struct Node* headNode)
{
FILE *fp=fopen(fileName,"r");//以讀的方式
if(fp==NULL)
{
//不存在,創(chuàng)建文件
fp=fopen(fileName,"w+");//以創(chuàng)建的方式打開
}
struct bookInfo tempData;
while(fscanf(fp,"%s\t%f\t%d\n",tempData.bookName,&tempData.price,&tempData.num)!=EOF)
//讀到這個變量里面去,不做格式控制,當(dāng)他讀到的時候,插入到鏈表Node里面
{
insertNodeFromHead(list,tempData);
}
fclose(fp);
}
調(diào)用一下
main函數(shù)中
int main()
{
list=createHead();
readInfoFromFile("bookinfo.txt",list); //主程序運行就讀取文件到list
while(1)
{
makeMenu();
keyDown();
system("pause");
system("cls");
}
return 0;
}
當(dāng)我們修改了東西的時候,往文件里面寫寫入
登記,就是插入鏈表
case 1:
具體的說就是 case 1里面增加 saveInfotoFile("bookinfo.txt",list);
void keyDown() //判斷用戶輸入的子菜單數(shù)字去判斷
{
int userKey=0; //默認(rèn)初始值為0退出
struct bookInfo tempBook;//產(chǎn)生一個臨時的變量儲存書籍信息
scanf("%d",&userKey);//用scanf獲得整數(shù) 一般是沒有什么坑的
//思考一下,判斷數(shù)字是0~7的數(shù)用什么處理
switch(userKey)
{
case 0://退出模塊,做一個人機界面 提醒
printf("【退出】\n");
break;
case 1://復(fù)制粘貼的操作,快速處理一下
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");
}
}
測試一下,發(fā)現(xiàn)寫了一個bookinfo.txt文件(工程目錄下)
那么case 2中瀏覽書籍的代碼更改就很容易了
先讀文件,然后在打印
瀏覽
case 2
case 2:
printf("【瀏覽】\n");
printList(list);
break;
attention:
1.鏈表節(jié)點Node與圖書結(jié)構(gòu)體變量的包含關(guān)系
2.文件的讀過程,用fscanf()函數(shù)往臨時圖書結(jié)構(gòu)體變量里面TempData,文件的所有內(nèi)容,都要通過結(jié)構(gòu)體變量的方式,循環(huán)讀取到末尾,保存一個TempData就調(diào)用一次 insertNodeFromHead插入函數(shù),組成鏈表
3.文件寫過程,用fprintf()函數(shù),對鏈表Node結(jié)構(gòu)體的指針pMove進行是否為空的操作,而不是對fp進行是否為空的操作,通過鏈表Node結(jié)構(gòu)體指針pMove訪問內(nèi)部的 圖書結(jié)構(gòu)體變量這個成員,再訪問圖書結(jié)構(gòu)體變量中的自己的成員
(套娃pMove->data.bookName)
4.注意什么時候是對list與headNode的區(qū)別與聯(lián)系,由于list是全局的headNode,每次需要更新整個鏈表,使用list,對全局影響。而在功能函數(shù)的封裝中,插入單個節(jié)點,刪除單個節(jié)點的函數(shù)中使用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節(jié)點中成員data結(jié)構(gòu)體變量中成員price,大于下一個節(jié)點
{
//交換
//需要臨時變量,這個臨時變量是一個 strcut bookInfo結(jié)構(gòu)體變量
struct bookInfo tempData=q->data;
q->data=(q->next->data);
q->next->data=tempData;
}
}
}
printList(headNode);
}
注意第二次for循環(huán)中的 循環(huán)終止條件是 q->next!=NULL,與外層循環(huán)的p!=NULL不一樣
調(diào)用一下就可以調(diào)試了
case 5:
printf("【排序】\n");
bubbleSortList(list);
刪除
我們是通過書籍名稱刪除
void deleteNodeByData()函數(shù)修改為 void deleteNodeByName()
case 6 直接調(diào)用刪除函數(shù)
case 6:
printf("【刪除】\n");
printf("請輸入刪除的書名\n");
scanf("%s",tempBook.bookName); //前面定義了一個tempBook臨時結(jié)構(gòu)體變量
deleteNodeByName(list,tempBook.bookName);
//涉及了數(shù)據(jù)的修改,要同步到文件
saveInfoToFile("bookinfo.txt",list);
查找,通過姓名
查找等效于鏈表的查找,找到節(jié)點后返回
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里面調(diào)用一下函數(shù),返回的這個指針拋出一個人機交互,找到與沒找到。
keydown()函數(shù)新聲明一個
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數(shù)量\n");
printf("%s\t%.1f\t%d\n",result->data.bookName,result->data.price,result->data.num);
}
break;
并且在 keyDown()函數(shù)初始化開始新增一個臨時指針變量
并且完善一下退出機制,盡管是while(1)死循環(huán),但是我們可以在case 0里面,輸入 exit(0)退出整個程序
更新過的keyDown()交互函數(shù)如下
void keyDown() //判斷用戶輸入的子菜單數(shù)字去判斷
{
int userKey=0; //默認(rèn)初始值為0退出
struct bookInfo tempBook;//產(chǎn)生一個臨時的變量儲存書籍信息
struct Node* result=NULL;
scanf("%d",&userKey);//用scanf獲得整數(shù) 一般是沒有什么坑的
//思考一下,判斷數(shù)字是0~7的數(shù)用什么處理
switch(userKey)
{
case 0://退出模塊,做一個人機界面 提醒
printf("【退出】\n");
exit(0);
break;
case 1://復(fù)制粘貼的操作,快速處理一下
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臨時結(jié)構(gòu)體變量
deleteNodeByName(list,tempBook.bookName);
//涉及了數(shù)據(jù)的修改,要同步到文件
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數(shù)量\n");
printf("%s\t%.1f\t%d\n",result->data.bookName,result->data.price,result->data.num);
}
break;
default:
printf("【erro】\n");
}
}
5.剩下的工作,借閱和歸還
這里的功能與其他部分增刪查改不一樣
①書籍存在,可以借閱,總數(shù)量-1,借閱成功,借閱前查詢是否有判斷,有這類書還要進行是否有庫存的判斷
②歸還,當(dāng)前數(shù)量+1
借閱case 3
case 3:
printf("【借閱】\n");
printf("請輸入借閱的書名\n");
scanf("%s",tempBook.bookName);
result=searchByName(list,tempBook.bookName);
if(result==NULL)
{
printf("沒有相關(guān)書籍,沒法借閱\n");
}
else
{
if(result->data.num>0)
{
result->data.num--;
printf("借閱成功!\n");
//借閱成功了,庫存減小,這里要更新一下list
saveInfoToFile("bookinfo.txt",list);
}
else
{
printf("借閱失敗,沒有庫存!\n");
}
}
break;
歸還case 4
歸還很簡單,復(fù)用一下代碼,并且可以簡化一下存在與否的查找
不需要判斷庫存是否大于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;
結(jié)束
總體代碼如下,僅適合調(diào)試不成功的對比,不要直接復(fù)制
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void makeMenu()
{
printf("-------------------------------\n");
printf("gdut圖書館里系統(tǒng)\n");
printf("\t0.退出系統(tǒng)\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;//這本書的數(shù)量
};
struct Node
{
struct bookInfo data;
struct Node* next; //有表頭的鏈表
};
struct Node* list=NULL;
//創(chuàng)建表頭: 表頭就是一個結(jié)構(gòu)體變量
struct Node* createHead()
{
//動態(tài)內(nèi)存申請
struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
//變量的基本規(guī)則,使用前必須初始化
headNode->next=NULL;//表頭可以不存數(shù)據(jù)
return headNode;
}
//創(chuàng)建節(jié)點: 為插入節(jié)點做準(zhǔn)備
//把用戶的數(shù)據(jù)作為參數(shù),傳入 變?yōu)榻Y(jié)構(gòu)體變量
struct Node* createNode(struct bookInfo data)
{
struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=data; //節(jié)點的數(shù)據(jù),用來做初始化
newNode->next=NULL;
return newNode;
}
//打印鏈表
void printList(struct Node* headNode)
{
struct Node* pMove=headNode->next; //打印表頭的下一個鏈表的內(nèi)容,我們用一個指針保存
printf("書名\t價格\t數(shù)量\n");
while(pMove)//循環(huán)指針指到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函數(shù),返回一個結(jié)構(gòu)體變量指針,創(chuàng)建一個newNode
newNode->next=headNode->next;
headNode->next=newNode;
}
void insertNodeFromTail(struct Node* headNode,struct bookInfo data)
{
struct Node* pMove=headNode;//創(chuàng)建一個用于移動的結(jié)構(gòu)體指針,指向頭節(jié)點
while(pMove->next!=NULL)
{
pMove=pMove->next;//不斷的判斷是否是表尾,并且移動到下一個節(jié)點,循環(huán)結(jié)束后就移動到表尾了
}
struct Node* newNode=createNode(data);
pMove->next=newNode;
}
void deleteNodeByName(struct Node* headNode, char *deletedbookName)
{
struct Node* posLeftNode=headNode;
struct Node* posNode=headNode->next;//刪除指針相鄰的節(jié)點,一前一后
//使用字符串比較函數(shù),進的include<string.h>
while(posNode!=NULL&&strcmp(posNode->data.bookName,deletedbookName)) //當(dāng)要刪除的節(jié)點不等于空節(jié)點,并且數(shù)據(jù)不是要刪除的數(shù)據(jù),節(jié)點向后移動
{
posLeftNode=posNode;
posNode=posLeftNode->next;//這兩個節(jié)點并排向前移動
}
if(posNode==NULL)
return; //沒有找到
else
{
printf("刪除成功!\n");
posLeftNode->next=posNode->next;
free(posNode);//然后釋放內(nèi)存
posNode=NULL;//刪除的節(jié)點置空
}
}
void saveInfoToFile(const char *fileName,struct Node* headNode)
{
FILE *fp=fopen(fileName,"w");//以寫的方式
struct Node* pMove=headNode->next;//獲得一個pMove節(jié)點,為第二個節(jié)點
while(pMove!=NULL)
{
fprintf(fp,"%s\t%.1f\t%d\n",pMove->data.bookName,pMove->data.price,pMove->data.num);
pMove=pMove->next;
//把數(shù)據(jù)輸出到文件 fprintf函數(shù),用對應(yīng)的格式
/*struct Node
{
struct bookInfo data;
struct Node* next; //有表頭的鏈表
};
*/
//容器中放入的是 結(jié)構(gòu)體指針,與書籍結(jié)構(gòu)體變量,所以通過鏈表這個結(jié)構(gòu)體找到 數(shù)據(jù)這個結(jié)構(gòu)體變量,在通過.運算符 訪問具體信息
}
fclose(fp);
}
void readInfoFromFile(const char *fileName,struct Node* headNode)
{
FILE *fp=fopen(fileName,"r");//以讀的方式
if(fp==NULL)
{
//不存在,創(chuàng)建文件
fp=fopen(fileName,"w+");//以創(chuàng)建的方式打開
}
struct bookInfo tempData;
while(fscanf(fp,"%s\t%f\t%d\n",tempData.bookName,&tempData.price,&tempData.num)!=EOF)
//讀到這個變量里面去,不做格式控制,當(dāng)他讀到的時候,插入到鏈表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節(jié)點中成員data結(jié)構(gòu)體變量中成員price,大于下一個節(jié)點
{
//交換
//需要臨時變量,這個臨時變量是一個 strcut bookInfo結(jié)構(gòu)體變量
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() //判斷用戶輸入的子菜單數(shù)字去判斷
{
int userKey=0; //默認(rèn)初始值為0退出
struct bookInfo tempBook;//產(chǎn)生一個臨時的變量儲存書籍信息
struct Node* result=NULL;
scanf("%d",&userKey);//用scanf獲得整數(shù) 一般是沒有什么坑的
//思考一下,判斷數(shù)字是0~7的數(shù)用什么處理
switch(userKey)
{
case 0://退出模塊,做一個人機界面 提醒
printf("【退出】\n");
exit(0);
break;
case 1://復(fù)制粘貼的操作,快速處理一下
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("沒有相關(guān)書籍,沒法借閱\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臨時結(jié)構(gòu)體變量
deleteNodeByName(list,tempBook.bookName);
//涉及了數(shù)據(jù)的修改,要同步到文件
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數(shù)量\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;
}