圖書館信息管理系統(tǒng)

圖書管理系統(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;
}
image-20210202174049321

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)存

image-20210201210520260

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插入,只需要一種插入方式(表頭法插入)

image-20210201211521759

第一個節(jié)點叫headNode,第二個節(jié)點就叫headnode->next

新插入的節(jié)點newNode,第一步連到headNode->next
第二部headNode連到newNode
image-20210202175751991
//代碼實現(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é)點)

image-20210202191617812
知道刪除節(jié)點左邊的節(jié)點要刪除這個節(jié)點
image-20210202191814709
就是要把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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容