zl程序教程

您现在的位置是:首页 >  后端

当前栏目

基于C语言(B 树索引)实现(控制台) 图书管理系统【100010728】

C语言索引管理系统 实现 基于 控制台 图书
2023-09-11 14:17:49 时间

基于 B 树为索引的图书管理系统

一、需求分析

1.1 问题描述

图书管理基本业务活动包括:对一本书的采编入库、清除库存、借阅和归还等等。试设计一个图书管理系统,将上述业务活动借助于计算机系统完成。

1.2 基本要求

每种书的登记内容至少包括书号、书名、著者、现存量和总库存量等五项。

作为演示系统,不必使用文件,全部数据可以都在内存存放。但是由于上述四项基本业务活动都是通过书号(即关键字)进行的,所以要用 B 树(2-3 树)对书号建立索引,以获得高效率。

系统应实现的操作及其功能定义如下:

① 采编入库:新购入一种书,经分类和确定书号之后登记到图书账目中去。如果这种书在帐中已有,则只将总库存量增加。

② 清除库存:某种书已无保留价值,将它从图书账目中注销。

③ 借阅:如果一种书的现存量大于零,则借出一本,登记借阅者的图书证号和归还期限。

④ 归还:注销对借阅者的登记,改变该书的现存量。

⑤ 显示:以凹入表的形式显示 B 树。这个操作是为了调试和维护的目的而设置的。下列 B 树的打印格式如下所示:

1.3 已完成情况

基本功能基本功能选做功能选做功能
图书入库已完成操作日志已完成
删除图书已完成查询作者著作已完成
借书已完成查询书籍详细信息和借阅记录已完成
还书已完成数据存储文件已完成
总结总结总结总结
1、图书管理系统的四个基本功能已经全部完成。;2、选择内容四个功能已完成三个功能。1、图书管理系统的四个基本功能已经全部完成。;2、选择内容四个功能已完成三个功能。1、图书管理系统的四个基本功能已经全部完成。;2、选择内容四个功能已完成三个功能。1、图书管理系统的四个基本功能已经全部完成。;2、选择内容四个功能已完成三个功能。

1.4 程序亮点

亮点一:实现了输入判断。当用户输入异常数据,系统会给出提示。

比如在功能菜单页面输入了 999,以及已存在图书号和借阅者号,更新库存时出错等提示

亮点二:菜单页面和打印索引结构页面更为直观

二、概要设计

2.1 数据类型的定义

2.2 功能模块关系

三、详细设计

3.1 数据类型定义

B 树的数据结构定义

/** B树的结点类型 **/
typedef struct BTNode {
    int keynum;                 // 关键字个数
    KeyType key[M+1];           // 关键字数组, key[0]未用
    struct BTNode *parent;      // 双亲结点指针
    struct BTNode *ptr[M+1];    // 孩子结点指针数组
    void *record[M+1];          // 记录指针向量,0号单元未用, 指向保存的数据
} BTNode, *BTree;               // B树的结点及指针类型
/** 返回的结果信息类型 **/
typedef struct result {
    BTree pt;                   // 指向找到的结点
    int i;                      // 在结点中的关键字位序(1 <= i <= m)
    int tag;                    // 1:查找成功 0:查找失败
} result, *resultptr;
/** 辅助打印索引结构类型 **/
typedef struct arr {
    int *thelast;               // 用于标记分隔符
    int max;                    // 最大值/层数
    int n;                      // 当前位/层
} arr, *array;

图书相关的数据结构定义

/** 借阅者结构体类型 **/
typedef struct reader {
    int readerId;               // 借阅者id
    char name[30];              // 借阅者姓名
} reader, *readerptr;           // 借阅者
/** 借阅记录结构类型 **/
typedef struct rcd {
    readerptr reader;           // 借阅者
    char borrowTime[25];        // 借书时间
    char returnTime[25];        // 归还时间
} rcd, *rcdptr;
/** 借阅记录的队列结点结构类型 **/
typedef struct rcdQueueNode {
    rcdptr data;                // 借阅记录的数据
    struct rcdQueueNode *next;  // 指向下一借阅记录的指针
} qNode;
/** 借阅记录队列结构类型 **/
typedef struct que {
    int count;                  // 借阅数量
    qNode *front;               // 头指针
    qNode *rear;                // 尾指针
} que, *queue;
/** 图书结构类型 **/
typedef struct book {
    int bookId;                 // 书号
    char name[30];              // 书名
    char writer[30];            // 著者
    int count;                  // 现存量
    int total;                  // 总库存量
    queue borrower;             // 借阅记录
} book, *bookptr;               // 结构体和指针的别名
/** 图书结点类型 **/
typedef struct bookNode {
    bookptr data;               // 图书数据
    struct bookNode *next;      // 指向下一结点指针
} bookNode, *bookNodeptr;
/** hash存储表结构 **/
typedef struct {
    bookNodeptr *rcd;           // 通过hash存放图书结点的数组
    int size;                   // 容量
    int count;                  // 已存在数
} HashTable, *Hashptr;

3.2 函数功能具体设计

  • 图书入库

  • 删除图书

  • 借书

  • 还书

  • 查看图书

  • 查看某著者所有著作

  • 查看图书列表

  • 查看借阅者列表

四、调试分析

4.1 问题分析与解决

难题一:借阅者的数据存储定义

本来是不打算使用 B 树来存储读者信息的,但是一想到当借阅者数量足够多时查找借阅者的难度应该挺大的,而且查询借阅者的难度和查询图书的难度应该保持一致,恰好 B 树能作为索引优化查询效率,因此决定使用 B 树来存储借阅者信息。

难题二:查询某一著者的所有著作

决定使用 B 树来存储借阅者信息时,我也想将著者也使用借阅者信息存储的,后面发现如果这样,那么在查询某一著者的所有著作时,难度会非常高,因为你需要去判断 B 树中的所有图书,有哪些图书的著者与要查找的著者相同的,即使实现了,效率上也不会很高。因此我决定用 hash 表存储著者的所有著者信息。在 hash 表中只需要存带有图书结点的链表,结点的成员是指向图书信息结构体的指针,这样一来占用的空间不用很大,而且在效率上也提高了很多。

难题三:数据结构体的定义

基于以往写大作业的经验来谈,我更倾向于将 B 树划分为更通用的数据结构,而不是将图书、借阅者等与其结合,这样会造成耦合过高,不利于维护,而且也不利于扩展。因此我借助 B 树中的 record 字段来标识关键字所指向的外部数据(图书和借阅者信息)。

难题四:数据存储入文件的处理

由于向文件中存储数据很难做到从尾部追加和删除,因此决定每次保存时都彻底的覆盖文件。

4.2 经验体会

最大的体会就是,如果要设计一个整体性功能完善的系统的话,一开始一定要构思好整体的逻辑,包括各种功能的具体定义、实现的合理性、交互的实用性以及功能和功能之间整体性。如果没有最初的构思,对于整个程序根本是无从下手的。首先你得定义好基础的数据结构,再到业务数据结构,然后设计功能函数,最后是设计好界面和调试试错,而功能与功能模块之间的关联性又是很强,而这些都是基于数据结构存储定义的,需要有很完全可用的数据结构才能够实现。

其次是 c 语言里,进行单元测试(单个函数的调试)是非常困难且非常麻烦的事情,因为函数与函数之间的关联性很强,耦合度非常高,如果你想单独调试一个函数,而这函数又与其他函数相连,首先你得有完整和合理的测试数据输入,再是测试函数,只有存在 main.c 函数时能完成链接生成 exe,这样你需要写很多冗余的测试函数。最好的方式当然是将底层全部铺好后一起测试,但是这样后期暴露的小 bug 会非常多。

最后是耐心非常重要。当测试数据需要重复输入,调试的断点需要在各处打时,耐心就显得尤为重要了。

五、用户使用说明

5.1 功能菜单

如果已经存在了之前的数据文件(index.dat, queue.dat, reader.dat),会出现以下提示

然后进入菜单界面

5.2 使用说明

根据输入的序号来进行各种操作

图中显示的序号意义

序号功能简介
1添加图书
2添加借阅者
3删除图书
4更新图书
5查看图书详细信息
6打印索引结构
7查找某一著者的所有图书
8查看图书列表
9查看订阅者列表
10摧毁 B 树
11借书
12还书
13退出

测试数据:

书名随机输入

著者名为 1 到 111 不等

库存为 1 到 111 不等

借阅者:

借阅者号借阅者姓名
1王明明
2王嘉然
3杜向晚

六、测试结果

6.1 图书入库

插入测试数据 35, 16, 18, 70, 5, 50, 22, 60, 13, 17, 12 , 45, 25, 42, 15, 90, 30, 7

  • 插入书号为 35 的图书

  • 插入书号为 16 的图书

  • 插入书号为 18 的图书

  • 插入书号为 70 的图书

  • 插入书号为 5 的图书

  • 插入书号为 50 的图书

  • 插入书号为 22 的图书

  • 插入书号为 60 的图书

  • 插入书号为 13 的图书

  • 插入书号为 17 的图书

  • 插入书号为 12 的图书

  • 插入书号为 45 的图书

  • 插入书号为 25 的图书

  • 插入书号为 42 的图书

  • 插入书号为 15 的图书

  • 插入书号为 90 的图书

  • 插入书号为 30 的图书

  • 插入书号为 7 的图书

6.2 删除图书

删除测试数据 45, 90, 50, 22, 42

插入完成后索引图为

  • 删除书号为 45 的图书

  • 删除书号为 90 的图书

  • 删除书号为 50 的图书

  • 删除书号为 22 的图书

  • 删除书号为 42 的图书

至此删除操作完成

6.3 添加借阅者

借阅者号借阅者姓名
1王明明
2王嘉然
3杜向晚

6.4 查看图书列表

6.5 借书

3 号借阅者杜向晚借书(书号为 5)

查看图书列表

6.6 查看图书详细信息

6.7 查看订阅者列表

6.8 还书

在这里插入图片描述

七、附录

7.1 头文件

BTree.h
# ifndef BTREE_H_INCLUDED
# define BTREE_H_INCLUDED
# include <stdio.h>
# endif // BTREE_H_INCLUDED
# define M 3                     // B树的阶,设为3
typedef int KeyType;            // 关键字类型
typedef enum Status {           // 表状态的枚举类
    SUCCESS,                    // 0-成功
    ERROR                       // 1-失败/错误
} Status;
/** B树的结点类型 **/
typedef struct BTNode {
    int keynum;                 // 关键字个数
    KeyType key[M+1];           // 关键字数组, key[0]未用
    struct BTNode *parent;      // 双亲结点指针
    struct BTNode *ptr[M+1];    // 孩子结点指针数组
    void *record[M+1];          // 记录指针向量,0号单元未用, 指向保存的数据
} BTNode, *BTree;               // B树的结点及指针类型
/** 返回的结果信息类型 **/
typedef struct result {
    BTree pt;                   // 指向找到的结点
    int i;                      // 在结点中的关键字位序(1 <= i <= m)
    int tag;                    // 1:查找成功 0:查找失败
} result, *resultptr;
/** 辅助打印索引结构类型 **/
typedef struct arr {
    int *thelast;               // 用于标记分隔符
    int max;                    // 最大值/层数
    int n;                      // 当前位/层
} arr, *array;
// 返回关键字k的位序
int search(BTree t, KeyType k);
// 分裂结点
void split(BTree t, int s, BTree *ap, void *rec);
// 创建新结点
void newRoot(BTree *p, BTree left, KeyType x, BTree right, void *rec);
// 插入
void insert(BTree t, int i, KeyType x, BTree p, void *rec);
// 找出Ai子树最下层非终端结点的最小关键字代替Ki
void successor(BTree *t, int i);
// 从结点中删除关键字key[i]
void removeKey(BTree t, int i);
// 调整BTree
void restore(BTree *root, BTree *t, int i);
// 为该位的索引结构添加标识并向下一层迁移
array append(array a, int last);
// 初始化一个array
array getArray();
// BTree的查找
resultptr searchBTree(BTree t, int k);
// 插入结点
Status insertBTree(BTree *t, KeyType k, BTree q, int i, void *rec);
// 删除结点
void deleteBTree(BTree *root, BTree *t, int i);
// 销毁BTree
Status destroyBTree(BTree t);
// 打印BTree索引结构
void printBTree(BTree t);
// 打印索引结构的功能函数
void print(BTree t, int depth, array last);
// 弹出当前位,回溯到上一层
array pop(array a);
// 初始化BTree结点
BTree getBTree();
// 初始化搜索结果信息
resultptr getResult(BTree t, int index, int tag);
Librarian.h
# ifndef LIBRARIAN_H_INCLUDED
# define LIBRARIAN_H_INCLUDED
# include <time.h>
# include <string.h>
# include <stdarg.h>
# include <stdlib.h>
# endif // LIBRARIAN_H_INCLUDED
# define HASH_TABLE_SIZE 1000
/** 借阅者结构体类型 **/
typedef struct reader {
    int readerId;               // 借阅者id
    char name[30];              // 借阅者姓名
} reader, *readerptr;           // 借阅者
/** 借阅记录结构类型 **/
typedef struct rcd {
    readerptr reader;           // 借阅者
    char borrowTime[25];        // 借书时间
    char returnTime[25];        // 归还时间
} rcd, *rcdptr;
/** 借阅记录的队列结点结构类型 **/
typedef struct rcdQueueNode {
    rcdptr data;                // 借阅记录的数据
    struct rcdQueueNode *next;  // 指向下一借阅记录的指针
} qNode;
/** 借阅记录队列结构类型 **/
typedef struct que {
    int count;                  // 借阅数量
    qNode *front;               // 头指针
    qNode *rear;                // 尾指针
} que, *queue;
/** 图书结构类型 **/
typedef struct book {
    int bookId;                 // 书号
    char name[30];              // 书名
    char writer[30];            // 著者
    int count;                  // 现存量
    int total;                  // 总库存量
    queue borrower;             // 借阅记录
} book, *bookptr;               // 结构体和指针的别名
/** 图书结点类型 **/
typedef struct bookNode {
    bookptr data;               // 图书数据
    struct bookNode *next;      // 指向下一结点指针
} bookNode, *bookNodeptr;
/** hash存储表结构 **/
typedef struct {
    bookNodeptr *rcd;           // 通过hash存放图书结点的数组
    int size;                   // 容量
    int count;                  // 已存在数
} HashTable, *Hashptr;
// 把整形(int)转换成字符串(char*)的形式
char *int2str(int num);
// 初始化队列
void initQueue(queue *q);
// 入队
int enQueue(queue q, rcdptr rdr);
// 出队
void deQueue(queue q);
// 获取队列头结点的数据
rcdptr getFront(queue q);
// 移除某借阅者id队列元素
int removeElem(queue q, int readerId);
// **********************
// 菜单
void menu();
// 清空缓冲区
void flush();
// 初始化图书信息
bookptr getBook(int bookId, char *name, char *writer, int total);
// 检查输入数据以及插入图书信息
int checkAndInsert(BTree *t, Hashptr table, KeyType k, void *rec);
// 打印所有图书信息
void printAllBook(BTree t);
// 打印所有图书信息功能类
void printBookIndex(BTree t);
// 打印图书详细信息
void printBookDetail(bookptr book);
// 打印单条图书信息
void printCompletBook(bookptr book);
// 打印单条图书信息2 差别只在于宽度
void printBook(bookptr book);
// 打印某著者的所有图书信息
int printBookByWriter(Hashptr table, char *name);
// 更新图书信息
int updateBook(Hashptr tb, bookptr book, char *nm, char *wrtr, int tot);
// 保存图书信息
void saveBTree(BTree t, FILE *index, FILE *queue);
// **********************
// 插入借阅者
int insertReader(BTree *t, void *rec);
// 打印所有借阅者信息
void printAllReader(BTree rt);
// 打印所有借阅者信息-功能函数
void printReaderIndex(BTree rt);
// 借书
int borrowBook(bookptr book, readerptr rdr);
// 还书
int returnBook(bookptr book, int readerid);
// 保存借阅者信息
void saveReader(BTree rt, FILE *fp_reader);
// **********************
// hash
int addHash(Hashptr table, bookptr book);
// hash函数
int hashFunc(char *data);
// 检测是否存在hash
int existHash(Hashptr table, bookptr book);
// 移除hash
int removeHash(Hashptr table, bookptr book);
// 拼接字符串
char *catchString(int n, ...);
// 写入日志
void writeLog(char *msg);
// 保存至文件
void save(BTree t, BTree rt);
// 从文件中加载数据
int load(BTree *t, BTree *rt, Hashptr tb);
// 从文件中加载借阅者数据
int loadReader(BTree *rt);
// 从文件中加载图书数据
int loadBTree(BTree *t, bookptr book);

7.2 树功能函数

BTree.c
# include "header/BTree.h"
# include <stdio.h>
# include <stdlib.h>
/**
* @brief 返回关键字k的大致位序(p->key[i-1]<k<=p->key[i])
*
* @param t BTree
  • @param k 关键字
* @return int (p->key[i-1]<k<=p->key[i])
          */
int search(BTree t, KeyType k) {
    int i = 1;
// 关键字范围内查找
    while(i <= t->keynum && t->key[i] < k) {
        i++;
    }
    return i;
}
/**
  • @brief 分裂结点,前一半留在原结点,后一半移入 ap 指向的结点
*
* @param t     BTree
* @param s     mid index
* @param ap    targe BTree
* @param rec   record data
*/
void split(BTree t, int s, BTree *ap, void *rec) {
    int i, j, n = t->keynum;
// 生成新结点
    (*ap) = getBTree();
// 改变孩子指针数组指向
    (*ap)->ptr[0] = t->ptr[s];
// 将结点后一半移向ap所指向的结点
    for (i = s + 1, j = 1; i <= n; i++, j++) {
        (*ap)->key[j] = t->key[i];
        (*ap)->record[j] = t->record[i];
        (*ap)->ptr[j] = t->ptr[i];
    }
    (*ap)->parent = t->parent;
    (*ap)->keynum = n - s;
    for (i = 0; i <= n - s; i++) {
// 修改子结点的双亲指针指向
        if ((*ap)->ptr[i]) {
            (*ap)->ptr[i]->parent = *ap;
        }
    }
// 前一半保留
    t->keynum = s - 1;
}
/**
* @brief
*
* @param a
* @param last
* @return array
*/
array append(array a, int last) {
// 如果满了则扩容
    if (a->n == a->max) {
        a->thelast = (int *)realloc(a->thelast, sizeof(int) * ((a->max) + 10));
    }
    a->max += 10;
    a->thelast[(a->n)++] = last;
    return a;
}
/**
* @brief Get the Array object
*
* @return array
*/
array getArray() {
    array a = (array)malloc(sizeof(struct arr));
    a->max = 10;
    a->n = 0;
    a->thelast = (int *)malloc(sizeof(int) * (a->max));
    return a;
}
/**
* @brief
*
* @param t BTree
  • @param k 待查关键字
  • @return resultptr 结果信息指针
*/
resultptr searchBTree(BTree t, int k) {
// p-指向待查结点 q-指向双亲
    BTree p = t, q = NULL;
    if(t == NULL) {
        return NULL;
    }
// isFound-记录是否找到结点, i-结点关键字大致位序
    int isFound = 0, i = 0;
    while (p != NULL && isFound == 0) {
// 结点关键字大致位序
            = search(p, k);
        if (i <= p->keynum && p->key[i] == k) {
// 找到待查关键字
            isFound = 1;
        } else {
// 指针下移
                = p;
// 从0开始
                = p->ptr[i-1];
        }
    }
    if (isFound) {
        return getResult(p, i, 1);
    } else {
        return getResult(q, i, 0);
    }
}
/**
  • @brief 创建新结点
*
* @param p     new BTNode
* @param left  the left part
  • @param x 关键字
* @param right the right part
* @param rec   record data
*/
void newRoot(BTree *p, BTree left, KeyType x, BTree right, void *rec) {
    (*p) = getBTree();
    (*p)->keynum = 1;
    (*p)->key[1] = x;
    (*p)->record[1] = rec;
    (*p)->ptr[0] = left;
    (*p)->ptr[1] = right;
// 如果左边部分需要接入新结点
    if(left) {
        left->parent = *p;
    }
// 如果右边部分需要接入新结点
    if(right) {
        right->parent = *p;
    }
// 双亲结点置空
    (*p)->parent = NULL;
}
/**
* @brief 插入结点, 将关键字x和新结点指针ap分别插入t->key[i], q->ptr[i]
*
    • @param t 待插入结点
    • @param i 插入的位序(i-1 < position <= i)
    • @param x 关键字
    • @param p 新结点指针
* @param rec record data
*/
void insert(BTree t, int i, KeyType x, BTree p, void *rec) {
    int j, n = t->keynum;
// 右移为新结点腾出空间
    for (j = n; j >= i; j--) {
        t->ptr[j + 1] = t->ptr[j];
        t->key[j + 1] = t->key[j];
        t->record[j + 1] = t->record[j];
    }
    t->ptr[i] = p;
    t->key[i] = x;
    t->record[i] = rec;
    if (p) {
        p->parent = t;
    }
    t->keynum++;
}
/**
  • @brief 插入, 将关键字 k 插入 t 树的 q 结点的第 i-1~i 之间
*
* @param t         BTree
    • @param k 关键字
    • @param q 插入的目标结点
    • @param i 关键字插入位序
* @param rec       record data
  • @return Status 成功返回 SUCCESS 否则返回 ERROR
*/
Status insertBTree(BTree *t, KeyType k, BTree q, int i, void *rec) {
// s-中位数, finished-是否完成插入操作, need-是否需要改变根结点
    int s, finished = 0, need = 0;
// x-关键字
    KeyType x;
// 新结点指针
    BTree ap;
    void *recd;
// 目标结点指向若空则置新
    if (q == NULL) {
        newRoot(t, NULL, k, NULL, rec);
    } else {
            = k;
        ap = NULL;
        recd = rec;
        while (need == 0 && finished == 0) {
            insert(q, i, x, ap, recd);
            if (q->keynum < M) {
// 关键字个数未超出M,标记完成插入操作
                finished = 1;
            } else {
// 关键字总数达到M,需要分裂结点
                    = (M + 1) / 2;
                split(q, s, &ap, recd);
// 中位数位序的关键字, 下一个循环插入到双亲结点中
                    = q->key[s];
                recd = q->record[s];
                if (q->parent) {
// 未分裂到根节点, 向上检查关键字总数是否超出M
                        = q->parent;
                        = search(q, x);
                } else {
// 分裂到根节点,需要产生新的根节点
                    need = 1;
                }
            }
        }
// 产生新的根节点
        if (need) {
            newRoot(t, q, x, ap, recd);
        }
    }
    return SUCCESS;
}
/**
  • @brief 找出 Ai 子树最下层非终端结点的最小关键字代替 Ki
*
* @param t
* @param i
*/
void successor(BTree *t, int i) {
    if((*t) == NULL) {
        return;
    }
    BTree temp = (*t)->ptr[i];
// 下移至最下层
    while (temp->ptr[0]) {
        temp = temp->ptr[0];
    }
// 替换Ki
    (*t)->key[i] = temp->key[1];
    (*t)->record[i] = temp->record[1];
// t转为最下层结点返回
    (*t) = temp;
}
/**
  • @brief 从结点中删除关键字 key[i]
*
  • @param t 目标节点
  • @param i 关键字位序
*/
void removeKey(BTree t, int i) {
// 左移关键字覆盖
    while (i < t->keynum) {
        t->key[i] = t->key[i + 1];
        t->record[i] = t->record[i + 1];
        t->ptr[i] = t->ptr[i + 1];
        i++;
    }
    t->keynum--;
}
/**
  • @brief 调整 BTree
*
    • @param root 根节点
    • @param t 目标节点
    • @param i 当前节点在双亲的位序
*/
void restore(BTree *root, BTree *t, int i) {
// 目标节点的双亲结点
    BTree par = (*t)->parent;
    if (par == NULL) {
// 若为根节点
        if(par == NULL) {
            if((*t)->keynum == 0) {
                (*root) = (*t);
                (*root)->parent = NULL;
// free(*t);
            }
        }
        return;
    }
// 目标节点在双亲结点的位序
    int j = 0;
    while (par->ptr[j] != (*t)) {
        j++;
    }
    if (j > 0 && par->ptr[j - 1]->keynum > (M - 1) / 2) {
// 左兄弟结点个数足够
// 目标节点的左兄弟结点
        BTree sib = par->ptr[j - 1];
        (*t)->keynum++;
        int n = (*t)->keynum;
        int k;
// 目标节点的关键字右移,为调整所得的新节点腾出空间
        for (k = n; k > 0; k--) {
            (*t)->key[k] = (*t)->key[k - 1];
            (*t)->record[k] = (*t)->record[k - 1];
            (*t)->ptr[k] = (*t)->ptr[k - 1];
        }
        (*t)->ptr[1] = (*t)->ptr[0];
        (*t)->ptr[0] = sib->ptr[sib->keynum];
        if((*t)->ptr[0] != NULL) {
            (*t)->ptr[0]->parent = (*t);
        }
// 将大于该关键字的关键字下移
        (*t)->key[1] = par->key[j];
        (*t)->record[1] = par->record[j];
// 将左兄弟结点中最大的关键字上移至双亲结点
        par->key[j] = sib->key[sib->keynum];
        par->record[j] = sib->record[sib->keynum];
// 删除左兄弟结点中的最大关键字
        removeKey(sib, sib->keynum);
    } else if (j >= 0 && par->keynum >= j + 1 && par->ptr[j + 1]->keynum > (M - 1) / 2) {
// 右兄弟结点个数足够
        BTree sib = par->ptr[j + 1];
        (*t)->keynum++;
// 直接把双亲结点的右一个关键字放入目标节点的最大位
        (*t)->key[(*t)->keynum] = par->key[j + 1];
        (*t)->record[(*t)->keynum] = par->record[j + 1];
        (*t)->ptr[(*t)->keynum] = sib->ptr[0];
        if((*t)->ptr[(*t)->keynum] != NULL) {
            (*t)->ptr[(*t)->keynum]->parent = (*t);
        }
// 右兄弟结点最小的上移
        par->key[j + 1] = sib->key[1];
        par->record[j + 1] = sib->record[1];
// 删除右兄弟结点的最小关键字(实际上是覆盖了位序为1的关键字, 因为左移后为0而0未使用)
        removeKey(sib, 0);
    } else {
// 左右兄弟结点都不够
        if (j != 0) {
// 目标节点是位于右边的结点
// 目标节点的左兄弟节点
            BTree sib = par->ptr[j - 1];
            ++sib->keynum;
// 双亲结点中分割目标节点和左兄弟结点的关键字下移至左兄弟结点
            sib->key[sib->keynum] = par->key[j];
            sib->record[sib->keynum] = par->record[j];
            sib->ptr[sib->keynum] = (*t)->ptr[0];
            par->keynum--;
// 循环变量
            int k;
// 双亲结点的关键字左移
            for (k = j; k < par->keynum; k++) {
                par->key[k] = par->key[k + 1];
                par->record[k] = par->record[k + 1];
                par->ptr[k] = par->ptr[k + 1];
            }
// 将目标节点的剩余信息合并到左兄弟结点中
            for (k = 1; k <= (*t)->keynum; k++) {
                sib->keynum++;
                sib->key[sib->keynum] = (*t)->key[k];
                sib->record[sib->keynum] = (*t)->record[k];
                sib->ptr[sib->keynum] = (*t)->ptr[k];
            }
// free(*t);
            (*t) = sib;
            for (int i = 0; i <= sib->keynum; i++) {
                if(sib->ptr[i]) {
                    sib->ptr[i]->parent = sib;
                }
            }
        } else if (j != M) {
// 目标节点是位于左边的结点
// 目标节点的右兄弟节点
            BTree sib = par->ptr[j + 1];
            int count = (*t)->keynum + 1;
            par->keynum--;
            sib->keynum += count;
// 循环变量
            int k;
// 右兄弟结点关键字右移
            for (k = sib->keynum; k > count; k--) {
                sib->key[k] = sib->key[k - count];
                sib->record[k] = sib->record[k - count];
                sib->ptr[k] = sib->ptr[k - count];
            }
// 双亲结点关键字下移到右兄弟结点
            sib->ptr[count] = sib->ptr[0];
            sib->key[count] = par->key[j + 1];
            sib->record[count] = par->record[j + 1];
// 双亲结点指向子节点的指针左移
            par->ptr[j] = par->ptr[j + 1];
// 双亲结点关键字左移
            for (k = j + 1; k <= par->keynum; k++) {
                par->key[k] = par->key[k + 1];
                par->record[k] = par->record[k + 1];
                par->ptr[k] = par->ptr[k + 1];
            }
// // 目标节点的剩余信息并入右兄弟结点
            for (k = 1; k <= (*t)->keynum; k++) {
                sib->key[k] = (*t)->key[k];
                sib->record[k] = (*t)->record[k];
                sib->ptr[k] = (*t)->ptr[k];
            }
            sib->ptr[0] = (*t)->ptr[0];
// free(*t);
            (*t) = sib;
            for (int i = 0; i <= sib->keynum; i++) {
                if(sib->ptr[i]) {
                    sib->ptr[i]->parent = sib;
                }
            }
        }
        (*t) = (*t)->parent;
        if((*t) == (*root) && (*root)->keynum == 0) {
            (*root) = (*root)->ptr[0];
            (*root)->parent = NULL;
            free(*t);
        }
    }
// 如果双亲结点也如此,以双亲结点为目标节点调整树
    if (par->keynum < (M - 1) / 2) {
        restore(root, &par, j);
    }
}
/**
  • @brief 删除,删除 B 树 root 上 t 结点的第 i 个关键字
*
    • @param root 根节点
    • @param t 目标节点
    • @param i 删除关键字位序
*/
void deleteBTree(BTree *root, BTree *t, int i) {
// 不是最下层的非终端节点
    if((*t)->ptr[i]) {
// 找出Ai子树最下层非终端结点的最小关键字代替Ki, t已转为最下层结点
        successor(t, i);
// 删除最下层非终端节点中的最小关键字
        deleteBTree(root, t, 1);
    } else {
// 是最下层非终端节点
        removeKey((*t), i);
        if((*t)->keynum < (M - 1) / 2) {
            restore(root, t, i);
        }
    }
}
/**
  • @brief 销毁 BTree
*
* @param t         BTree
* @return Status
          */
Status destroyBTree(BTree t) {
    if(t == NULL) {
        return ERROR;
    }
    int i = 0;
    for (i = 0; i <= t->keynum; i++) {
        if(t->ptr[i]) {
// 递归销毁
            destroyBTree(t->ptr[i]);
        }
    }
    free(t);
    return SUCCESS;
}
/**
  • @brief 打印 BTree 索引结构
*
* @param t     BTree
*/
void printBTree(BTree t) {
    if (t == NULL) {
        return;
    }
    array last = getArray();
    print(t, 1, last);
}
/**
  • @brief 将层数返回一层
*
* @param a
* @return array
          */
array pop(array a) {
    if (a == NULL || a->n == 0) {
        return a;
    }
    a->n -= 1;
    return a;
}
/**
  • @brief 打印索引结构如下所示
|
|------>|
|       |------>|
|       |       |->1
|       |->2
|       |------>|
|               |->3
|->4
|------>|
|------>|
|       |->5
|->6
|------>|
|       |->7
|->8
|------>|
|->9
|->10
*
* @param t     BTree
  • @param depth 打印的深度
  • @param last 存放关键字位序的数组
*/
void print(BTree t, int depth, array last) {
    int i, j;
    if(t == NULL) {
        return;
    }
// 第一个|
    printf("|\n");
    for (i = 0; i <= t->keynum; i++) {
// 打印最下层结点关键字
        if(i != 0 && (t->key[i] != -1)) {
            for (j = 0; j < depth - 1; j++) {
                if(!(last->thelast[j])) {
                    printf("|");
                } else {
                    printf(" ");
                }
                printf("\t");
            }
            printf("|->");
            printf("%d\n", t->key[i]);
        }
        if((t->ptr[i]) != NULL) {
            for (j = 0; j < depth - 1; j++) {
                if(!(last->thelast[j])) {
                    printf("|");
                } else {
                    printf(" ");
                }
                printf("\t");
            }
            printf("|------>");
            if(i == t->keynum) {
                append(last, 1);
            } else {
                append(last, 0);
            }
            print(t->ptr[i], depth + 1, last);
            pop(last);
        }
    }
}
/**
  • @brief 初始化 BTree 结点
*
  • @return BTree BTree 结点
*/
BTree getBTree() {
    BTree t = (BTree)malloc(sizeof(BTNode));
    if(t == NULL) {
        return NULL;
    }
    t->parent = NULL;
    t->keynum = 0;
    for (int i = 0; i <= M; i++) {
        t->ptr[i] = NULL;
        t->key[i] = -1;
        t->record[i] = NULL;
    }
    return t;
}
/**
* @brief Get the Result object
*
    • @param t 指向找到的结点
    • @param index 结点位序
    • @param tag 是否找到,1:查找成功 0:查找失败
* @return resultptr
          */
resultptr getResult(BTree t, int index, int tag) {
    resultptr result = (resultptr)malloc(sizeof(struct result));
    result->pt = t;
    result->i = index;
    result->tag = tag;
    return result;
}

7.3 图书管理功能函数

Librarian.c
# include "header/BTree.h"
# include "header/Librarian.h"
/**
  • @brief 把整形(int)转换成字符串(char*)的形式
*
  • @param num 整形数字
  • @return char* 字符串形式
*/
char *int2str(int num) {
    char *res = (char *)malloc(sizeof(char) * 50);
    itoa(num, res, 10);
    return res;
}
/**
* @brief Get the Book object
*
* @param name
* @param writer
* @param total
* @return bookptr
*/
bookptr getBook(int bookId, char *name, char *writer, int total) {
    bookptr p = (bookptr)malloc(sizeof(book));
    p->bookId = bookId;
    p->count = p->total = total;
    strcpy(p->name, name);
    strcpy(p->writer, writer);
    initQueue(&(p->borrower));
    return p;
}
/**
  • @brief 初始化队列
*
* @param q
*/
void initQueue(queue *q) {
    (*q) = (queue)malloc(sizeof(que));
    (*q)->count = 0;
    (*q)->rear = (*q)->front = NULL;
}
/**
  • @brief 入队
*
  • @param q 队列
* @param rdr   the node data
* @return int  1-success 0-error
          */
int enQueue(queue q, rcdptr rdr) {
    if (q == NULL || rdr == NULL) {
        return 0;
    }
// save the node data
    qNode *node = (qNode *)malloc(sizeof(qNode));
    node->data = rdr;
    node->next = NULL;
    if (q->rear) {
        q->rear->next = node;
    }
    q->rear = node;
    if (q->front == NULL) {
        q->front = node;
    }
    q->count++;
    return 1;
}
/**
  • @brief 出队
*
  • @param q 队列
*/
void deQueue(queue q) {
    if(q == NULL || q->front == NULL) {
        return;
    }
    qNode *p = q->front;
    q->front = p->next;
    free(p);
        = NULL;
    q->count--;
}
/**
* @brief Get the Front object
*
* @param q         the queue
* @return rcdptr   the front data of the queue
*/
rcdptr getFront(queue q) {
    if(q == NULL || q->front == NULL) {
        return NULL;
    }
    return q->front->data;
}
/**
* @brief remove the elem of the queue by readerId
*
* @param q         the queue
  • @param readerId 借阅者 id
* @return int      success return 1 else return 0
                                      */
    int removeElem(queue q, int readerId) {
    if(q == NULL || q->front == NULL) {
        return 0;
    }
    qNode *p = q->front;
    if(p->data->reader->readerId == readerId) {
        q->front = p->next;
        free(p);
        q->count--;
        return 1;
    } else {
        while(p->next) {
            if(p->next->data->reader->readerId == readerId) {
                qNode *temp = p->next;
                p->next = temp->next;
                free(temp);
                q->count--;
                return 1;
            }
                = p->next;
        }
    }
    return 0;
}
/**
  • @brief 菜单
*
*/
void menu() {
    printf("\n                 ||****************************************MENU****************************************||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||*********************************Librarian System***********************************||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||====================================================================================||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Please enter your choice<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||====================================================================================||\n");
    printf("                 ||                     1.Add Book                   2.Add Reader                      ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||                     3.Delete Book                4.Update Book                     ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||                     5.Check Book Detail Message  6.Print Tree Struction            ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||                     7.Find Book List of Writer   8.Check Book List                 ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||                     9.Check Reader List          10.Destory BTree                  ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||                     11.Borrow Book               12.Return Book                    ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||                     13.Exit                                                        ||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||====================================================================================||\n");
    printf("                 ||                                                                                    ||\n");
    printf("                 ||****************************************END*****************************************||\n");
    printf("                 ||->>      Please input your option:");
}
/**
* @brief fulsh
*
*/
void flush() {
    char c;
    while ((c = getchar()) != 10 && c != EOF);
}
/**
  • @brief 检查输入并插入数据
*
* @param t         BTree
* @param table     hashtable
  • @param k 关键字 k
* @param rec
*/
int  checkAndInsert(BTree *t, Hashptr table, KeyType k, void *rec) {
    resultptr p = searchBTree(*t, k);
    if (p->tag) {
        printf("\n\n\n\n\n||->>                         insert book failed! %d exist\n", k);
        free(p);
        return 0;
    } else {
        insertBTree(t, k, p->pt, p->i, rec);
// add hash
        addHash(table, (bookptr)rec);
        time_t t = time(NULL);
        char time[25];
// 格式化时间
        strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&t));
        char *msg = catchString(4, time, "      Add new book ", ((bookptr)rec)->name, "\n");
// 打印日志
        writeLog(msg);
        free(msg);
    }
    free(p);
    return 1;
}
/**
  • @brief 打印所有图书信息
*
* @param t BTree
*/
void printAllBook(BTree t) {
    printf("\n\t--------------------------------------------------------------------------------------------\n");
    printf("\t|   bookId    |         bookName      |        bookWriter     |   bookCount  |   bookTotal |\n");
    printf("\t--------------------------------------------------------------------------------------------\n");
    printBookIndex(t);
}
/**
  • @brief 打印所有图书信息-功能实现类
*
* @param t BTree
*/
void printBookIndex(BTree t) {
    if (t) {
        printBookIndex(t->ptr[0]);
        for (int i = 1; i <= t->keynum; i++) {
            printBook((bookptr)(t->record[i]));
            printBookIndex(t->ptr[i]);
        }
    }
}
/**
  • @brief 打印图书的详细信息(借阅情况)
*
* @param book book data
*/
void printBookDetail(bookptr book) {
    printf("\n\t--------------------------------------------------------------------------------------------\n");
    printf("\t|   bookId    |         bookName      |        bookWriter     |   bookCount  |   bookTotal |\n");
    printf("\t--------------------------------------------------------------------------------------------\n");
// 图书信息
    printCompletBook(book);
    printf("\tThe borrower list:\n");
    qNode *qp = book->borrower->front;
    printf("\t---------------------------------------------------------------------------------\n");
    printf("\t|     Id      |     Name     |        BorrowTime       |         ReturnTime     |\n");
    printf("\t---------------------------------------------------------------------------------\n");
// 打印借阅记录信息
    while (qp) {
        readerptr rdr = qp->data->reader;
        printf("\t|  %-8d   |   %-8s   |   %s   |   %s  |\n", rdr->readerId, rdr->name, qp->data->borrowTime, qp->data->returnTime);
        printf("\t---------------------------------------------------------------------------------\n");
        qp = qp->next;
    }
}
/**
  • @brief 打印单条图书信息
*
* @param book book
*/
void printCompletBook(bookptr book) {
    printf("\t|  %-8d   |   %-15s     |   %-15s     |   %-8d   |   %-8d  |\n", book->bookId, book->name, book->writer, book->count, book->total);
    printf("\t--------------------------------------------------------------------------------------------\n");
}
/**
  • @brief 打印单条图书信息 2
*
* @param book
*/
void printBook(bookptr book) {
    printf("\t|  %-8d   |   %-15.15s     |   %-15.15s     |   %-8d   |   %-8d  |\n", book->bookId, book->name, book->writer, book->count, book->total);
    printf("\t--------------------------------------------------------------------------------------------\n");
}
/**
  • @brief 打印某作者的所有图书信息
  • 通过 hash 作者名存储图书数据,可以直接找出该作者的所有图书作品信息
*
* @param table hashtable
* @param name  writer name
* @return int  success return 1 else return error
                                  */
    int printBookByWriter(Hashptr table, char *name) {
    if (table == NULL) {
        return 0;
    }
// get index
    int index = hashFunc(name) % table->size;
    bookNodeptr p = table->rcd[index];
    printf("\n\t--------------------------------------------------------------------------------------------\n");
    printf("\t|   bookId    |         bookName      |        bookWriter     |   bookCount  |   bookTotal |\n");
    printf("\t--------------------------------------------------------------------------------------------\n");
    while (p) {
        printBook(p->data);
            = p->next;
    }
    return 1;
}
/**
  • @brief 更新图书信息
*
* @param tb        hashtable point
* @param book      book point
* @param nm        name
* @param wrtr      writer
* @param tot       total
* @return int success return 1 else return 0
                                 */
    int updateBook(Hashptr tb, bookptr book, char *nm, char *wrtr, int tot) {
// 新的总量小于剩余量时更新失败
    if(tb == NULL || book == NULL || !nm || !wrtr || tot < (book->total - book->count)) {
        return 0;
    }
    removeHash(tb, book);
    strcpy(book->name, nm);
    strcpy(book->writer, wrtr);
// 新增的, 更新库存和总库存
    int inc = tot - book->total;
    book->count += inc;
    book->total = tot;
// update hash data
    addHash(tb, book);
    return 1;
}
/**
  • @brief 保存数据至文件
*
* @param t     book   BTree
* @param rt    reader Btree
*/
void save(BTree t, BTree rt) {
    FILE *fp_index = fopen("index.dat", "wb");
    FILE *fp_queue = fopen("queue.dat", "wb");
    FILE *fp_reader = fopen("reader.dat", "wb");
    saveBTree(t, fp_index, fp_queue);
    saveReader(rt, fp_reader);
    fclose(fp_reader);
    fclose(fp_index);
    fclose(fp_queue);
}
/**
  • @brief 保存图书信息
*
* @param t         BTree
* @param index     file point index
* @param queue     file point queue
*/
void saveBTree(BTree t, FILE *index, FILE *queue) {
    if(t == NULL) {
        return;
    }
    bookptr book;
    BTree visit;
    for (int i = 1; i <= t->keynum; i++) {
        book = (bookptr)(t->record[i]);
        if(book == NULL || book->bookId == 0) {
            continue;
        }
// 向index.dat中写入book data
        fwrite(book, sizeof(struct book), 1, index);
        int count = book->borrower->count;
// 向queue.dat写入借阅数量
        fwrite(&count, sizeof(int), 1, queue);
        qNode *p = book->borrower->front;
        while (p) {
// 向queue.dat写入借阅信息
            fwrite(p->data->reader, sizeof(reader), 1, queue);
            fwrite(p->data->borrowTime, sizeof(char) * 30, 1, queue);
            fwrite(p->data->returnTime, sizeof(char) * 30, 1, queue);
                = p->next;
        }
    }
    for (int i = 0; i <= t->keynum && t->ptr[i]; i++) {
// 向下层递归save
        visit = t->ptr[i];
        saveBTree(visit, index, queue);
    }
}
/**
* @brief
*
* @param rt
* @param fp_reader
*/
void saveReader(BTree rt, FILE *fp_reader) {
    if (rt == NULL) {
        return;
    }
    readerptr rdr = NULL;
// 访问节点
    BTree visit = NULL;
// 向reader.dat写入借阅者信息
    for (int i = 1; i <= rt->keynum; i++) {
        rdr = (readerptr)(rt->record[i]);
        fwrite(rdr, sizeof(reader), 1, fp_reader);
    }
// 递归保存借阅者信息
    for (int i = 0; i <= rt->keynum && rt->ptr[i]; i++) {
        visit = rt->ptr[i];
        saveReader(visit, fp_reader);
    }
}
/**
  • @brief 从文件中加载数据
*
* @param t     book BTree
* @param rt    reader BTree
* @param tb    hashtable
* @return int  success return 1 else return 0
                                  */
    int load(BTree *t, BTree *rt, Hashptr tb) {
    if(t == NULL || rt == NULL || tb == NULL) {
        return 0;
    }
    FILE *fp_index = fopen("index.dat", "rb");
    FILE *fp_reader = fopen("queue.dat", "rb");
// not file point
    if(!fp_index || !fp_reader) {
        return 0;
    }
// load reader data
    loadReader(rt);
// 重名
    bookptr book = (bookptr)malloc(sizeof(struct book));
    int result = 0;
    readerptr rdp = (readerptr)malloc(sizeof(reader));
// load book data
    while(fread(book, sizeof(struct book), 1, fp_index)) {
        result = loadBTree(t, book);
        addHash(tb, book);
// 借阅队列
        initQueue(&(book->borrower));
        int cnt = 0;
        fread(&cnt, sizeof(int), 1, fp_reader);
        while(cnt-- >= 0) {
// load record
            rcdptr rc = (rcdptr)malloc(sizeof(rcd));
            fread(rdp, sizeof(reader), 1, fp_reader);
            fread(rc->borrowTime, sizeof(char) * 30, 1, fp_reader);
            fread(rc->returnTime, sizeof(char) * 30, 1, fp_reader);
            resultptr res = searchBTree(*rt, rdp->readerId);
            if(res->tag == 1) {
                rc->reader = (readerptr)(res->pt->record[res->i]);
                enQueue(book->borrower, rc);
            } else {
// 已经load了,rt里应该都有
                insertReader(rt, rdp);
                rc->reader = rdp;
                enQueue(book->borrower, rc);
                rdp = (readerptr)malloc(sizeof(reader));
            }
        }
        book = (bookptr)malloc(sizeof(struct book));
    }
    free(rdp);
    free(book);
    fclose(fp_reader);
    fclose(fp_index);
    return result;
}
/**
  • @brief 加载借阅者数据
*
* @param rt    BTree point of reader
* @return int  success return 1 else return 0
                                  */
    int loadReader(BTree *rt) {
    FILE *fp_reader = fopen("queue.dat", "rb");
// not exist the file
    if(fp_reader == NULL) {
        return 0;
    }
    readerptr reader = (readerptr)malloc(sizeof(struct reader));
// overflow
    if(reader == NULL) {
        return 0;
    }
    int res = 0;
// 先查找,找不到再插入
    while(fread(reader, sizeof(reader), 1, fp_reader)) {
        resultptr result = (resultptr) searchBTree(*rt, reader->readerId);
// not find
        if(result->tag == 0) {
            res = insertBTree(rt, reader->readerId, result->pt, result->i, reader);
            reader = (readerptr)malloc(sizeof(struct reader));
        }
    }
// close the file stream
    fclose(fp_reader);
    free(reader);
    return res;
}
/**
  • @brief 加载图书数据
*
* @param t     BTree
* @param book  book data point
* @return int  success return 1 else return 0
                                  */
    int loadBTree(BTree *t, bookptr book) {
    resultptr res = searchBTree(*t, book->bookId);
    int result = 1;
    if (res->tag == 0) {
        result = (insertBTree(t, book->bookId, res->pt, res->i, book) == SUCCESS);
    }
    return result;
}
/**
  • @brief 拼接字符串
*
  • @param n 数目
  • @param … 可变参
* @return char*
          */
char *catchString(int n, ...) {
// 获取变参
    va_list list;
// 读取变参,遍历和读取栈区中的参数列表
    va_start(list, n);
    char *msg = (char *)malloc(sizeof(char) * 70);
    char *p = NULL;
    int i = 0;
    for (; i < 70; i++) {
        msg[i] = 0;
    }
    while (n--) {
// 使list指针指向变参列表中的下一个参数, 并返回其值
            = va_arg(list, char *);
// 拼接字符串
        strcat(msg, p);
    }
// 允许函数返回
    va_end(list);
    return msg;
}
/**
  • @brief 打印日志
*
* @param msg msg
*/
void writeLog(char *msg) {
    FILE *p = fopen("Librarian.log", "a");
    if (p) {
        fputs(msg, p);
    }
    fclose(p);
}
/**
* @brief hash
*
* @param table hashtable
  • @param book 图书信息
* @return int  if exist return 0 else return 1
                                   */
    int addHash(Hashptr table, bookptr book) {
        if (table == NULL || book == NULL) {
            return 0;
        }
// check if is exist
        if (existHash(table, book)) {
            return 0;
        }
        int index = hashFunc(book->writer) % table->size;
        bookNodeptr p = (bookNodeptr)malloc(sizeof(bookNode));
// save data to hashtable
        p->data = book;
        p->next = table->rcd[index];
        table->rcd[index] = p;
        table->count++;
        return 1;
    }
/**
* @brief check is exist hash
*
* @param table hashtable
* @param book  data
* @return int  1-exist 0-unexist
*/
int existHash(Hashptr table, bookptr book) {
//  hash
    int index = hashFunc(book->writer) % table->size;
    bookNodeptr p = table->rcd[index];
    if (p == NULL) {
        return 0;
    }
    while (p->next) {
        if (p->next->data->bookId == book->bookId) {
            return 1;
        }
            = p->next;
    }
    return 0;
}
/**
* @brief hash func
*
* @param data      hash data
* @return int      result
*/
int hashFunc(char *data) {
    int t = 0;
    for (int i = 0; data[i]; i++) {
        += data[i];
    }
    return t ^ (t >> 16);
}
/**
  • @brief 移除 hash
*
* @param table hashtable
* @param book  book data
* @return int  success return 1 else return 0
                                  */
    int removeHash(Hashptr table, bookptr book) {
// get index
    int index = hashFunc(book->writer) % table->size;
    bookNodeptr p = table->rcd[index];
    if (p == NULL) {
        return 0;
    }
// find it
    if (p->data->bookId == book->bookId) {
        bookNodeptr temp = p;
        table->rcd[index] = p->next;
        free(temp);
        return 1;
    }
// not find but can find the next to find it
    while (p->next) {
        if (p->next->data->bookId == book->bookId) {
            bookNodeptr temp = p->next;
            p->next = temp->next;
            free(temp);
            return 1;
        }
            = p->next;
    }
    return 0;
}
/**
  • @brief 新增借阅者信息
*
* @param t     BTree
* @param rec   reader data point
*/
int insertReader(BTree *t, void *rec) {
    readerptr rdp = (readerptr)rec;
    resultptr res = searchBTree(*t, rdp->readerId);
// 如果找到
    if(res->tag) {
        printf("\n\n\n\n\n||->>                         insert reader failed! %d exist\n", rdp->readerId);
        free(res);
        return 0;
    } else {
        insertBTree(t, rdp->readerId, res->pt, res->i, rec);
        time_t t = time(NULL);
        char time[25];
// format time style
        strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&t));
        char id[10];
        itoa(rdp->readerId, id, 10);
        char *msg = catchString(4, time, "      Add new reader ", id, "\n");
// write log
        writeLog(msg);
        free(msg);
        free(res);
        return 1;
    }
}
/**
  • @brief 打印所有借阅者信息
*
* @param rt BTree
*/
void printAllReader(BTree rt) {
    printf("\t------------------------------\n");
    printf("\t|     Id      |     Name     |\n");
    printf("\t------------------------------\n");
    printReaderIndex(rt);
}
/**
  • @brief 打印所有借阅者信息-功能信息
*
* @param rt BTree
*/
void printReaderIndex(BTree rt) {
    if (rt) {
        printReaderIndex(rt->ptr[0]);
        int i = 1;
        for (; i <= rt->keynum; i++) {
            readerptr rdr = (readerptr)(rt->record[i]);
            printf("\t|  %-8d   |   %-8s   |\n", rdr->readerId, rdr->name);
            printf("\t------------------------------\n");
// 递归打印子节点
            printReaderIndex(rt->ptr[i]);
        }
    }
}
/**
  • @brief 借书
*
* @param book  book data
* @param rdr   reader data point
* @return int success return 1 else return 0
                                 */
    int borrowBook(bookptr book, readerptr rdr) {
// 库存不足
    if (book == NULL || book->count <= 0) {
        return 0;
    }
    rcdptr rc = (rcdptr)malloc(sizeof(rcd));
    rc->reader = rdr;
    time_t t = time(NULL);
    strftime(rc->borrowTime, 25, "%Y-%m-%d %H:%M:%S", localtime(&t));
// 借书时限为30天
    time_t t2 = t + 30 * 24 * 60 * 60;
    strftime(rc->returnTime, 25, "%Y-%m-%d %H:%M:%S", localtime(&t2));
    enQueue(book->borrower, rc);
    book->count--;
    return 1;
}
/**
  • @brief 归还图书
*
* @param book      book data
  • @param readerid 借阅者 id
* @return int success return 1 else return 0
                                 */
    int returnBook(bookptr book, int readerid) {
    if (book == NULL) {
        return 0;
    }
    if (removeElem(book->borrower, readerid)) {
        book->count++;
        return 1;
    }
    return 0;
}

7.4 主函数

main.c
# include <stdio.h>
# include <stdlib.h>
# include "header/BTree.h"
# include "header/Librarian.h"
int main() {
    int option, res;
    BTree t = getBTree();
    BTree rdrt = getBTree();
    Hashptr tb = (Hashptr)malloc(sizeof(HashTable));
    tb->count = 0;
    tb->size = HASH_TABLE_SIZE;
    tb->rcd = (bookNodeptr *)malloc(sizeof(bookNodeptr) * tb->size);
    for(int i = 0; i < HASH_TABLE_SIZE; i++) {
        tb->rcd[i] = NULL;
    }
    if(load(&t, &rdrt, tb)) {
        printf("\n\n\n\n\n||->>                         load data success! Please enter to get the menu!");
        flush();
        system("cls");
    }
    menu();
    scanf("%d", &option);
    while(option != 13) {
        switch (option) {
// 添加图书
        case 1: {
            int bookId;
            char name[30];
            char writer[30];
            int total;
            system("cls");
            flush(stdin);
            printf("the bookId is ?\n");
            scanf("%d", &bookId);
            printf("the book name is ?\n");
            scanf("%s", name);
            printf("the writer name is ?\n");
            scanf("%s", writer);
            printf("what is the total?\n");
            scanf("%d", &total);
            bookptr book = getBook(bookId, name, writer, total);
            system("cls");
            res = checkAndInsert(&t, tb, book->bookId, book);
            save(t, rdrt);
            if(res == 1) {
                printf("\n\n\n\n\n||->>                         insert book success! Please enter to get the menu!");
            }
            break;
        }
// 添加借阅者
        case 2: {
            int userId;
            char readerName[20];
            system("cls");
            printf("the reader id is ?\n");
            scanf("%d", &userId);
            printf("the reader name is ?\n");
            scanf("%s", readerName);
            system("cls");
            readerptr reader = (readerptr)malloc(sizeof(struct reader));
            reader->readerId = userId;
            strcpy(reader->name, readerName);
            res = insertReader(&rdrt, reader);
            save(t, rdrt);
            if(res == 1) {
                printf("\n\n\n\n\n||->>                         insert reader success! Please enter to get the menu!");
            }
            break;
        }
// 删除图书
        case 3: {
            system("cls");
            printf("input the bookId of which you want to delete:\n");
            int bookId;
            scanf("%d", &bookId);
            system("cls");
            resultptr result = searchBTree(t, bookId);
            if(result->tag == 0) {
                printf("not find the book(id is %d)\n", bookId);
            } else {
                char bookName[50];
                strcpy(bookName, ((bookptr)(result->pt->record[result->i]))->name);
                removeHash(tb, (bookptr)((result)->pt->record[result->i]));
                deleteBTree(&t, &(result->pt), result->i);
                printf("\n\n\n\n\n||->>                         delete book success! Please enter to get the menu!");
                time_t tm = time(NULL);
                char time[25];
                strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&tm));
                char *msg = catchString(4, time, "      delete book ", bookName, "\n");
                writeLog(msg);
                free(msg);
            }
            save(t, rdrt);
            break;
        }
// 更新图书
        case 4: {
            system("cls");
            printf("input the booId of which you want to update:\n");
            int updateId, updateCount;
            char name[50];
            char writer[50];
            scanf("%d", &updateId);
            resultptr updateResult =  searchBTree(t, updateId);
            if(updateResult->tag == 0) {
                printf("not find the book(id is %d)\n", updateId);
            } else {
                system("cls");
                printf("the book name is ?\n");
                scanf("%s", name);
                printf("the writer name is ?\n");
                scanf("%s", writer);
                printf("what is the total?\n");
                scanf("%d", &updateCount);
                if(updateBook(tb, (bookptr)(updateResult->pt->record[updateResult->i]), name, writer, updateCount)) {
                    time_t tm = time(NULL);
                    char time[25],num[10];
                    itoa(updateId,num,10);
                    strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&tm));
                    char *msg = catchString(4, time, "      Update book ", num, "\n");
                    writeLog(msg);
                    free(msg);
                    printf("\n\n\n\n\n||->>                         update book success! Please enter to get the menu!");
                } else {
                    printf("\n\n\n\n\n||->>                         update book failed! Please enter to get the menu!");
                }
            }
            save(t, rdrt);
            break;
        }
// 查看图书详情
        case 5: {
            system("cls");
            printf("please input the bookId of which you want to see detail:\n");
            int bookId;
            scanf("%d", &bookId);
            resultptr result =  searchBTree(t, bookId);
            if(result->tag == 0) {
                printf("not find the book(id is %d)\n", bookId);
            } else {
                printBookDetail((bookptr)(result->pt->record[result->i]));
            }
            break;
        }
// 打印索引结构
        case 6: {
            system("cls");
            printf("choose the book(B) or the reader(R): \n");
            char choose;
            flush(stdin);
            while((choose = getchar()) != 'B' && choose != 'b' && choose != 'R' && choose != 'r') {
                flush();
                printf("please input B or R \n");
            }
            printf("\n");
            BTree temp = (choose == 'B' || choose == 'b') ? t : rdrt;
            printBTree(temp);
            break;
        }
// 查看某个作者的所有图书
        case 7: {
            system("cls");
            char writer[30];
            printf("input the writer you want to see \n");
            scanf("%s", writer);
            system("cls");
            printBookByWriter(tb, writer);
            break;
        }
// 查看图书列表
        case 8: {
            system("cls");
            printAllBook(t);
            break;
        }
// 查看借阅者列表
        case 9: {
            system("cls");
            printAllReader(rdrt);
            break;
        }
// 销毁索引
        case 10: {
            system("cls");
            printf("if you want to destroy the index, which will remove all data(y/n):\n");
            char choose;
            flush(stdin);
            while((choose = getchar()) != 'Y' && choose != 'y' && choose != 'N' && choose != 'n') {
                flush();
                printf("please input y or n \n");
            }
            if(choose == 'Y' || choose == 'y') {
                destroyBTree(t);
                    = getBTree();
                printf("\n\n\n\n\n||->>                         destroy successfully! \n");
                save(t, rdrt);
                time_t tm = time(NULL);
                char time[25];
                strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&tm));
                char *msg = catchString(2,time,"      Destroy all.\n");
                writeLog(msg);
                free(msg);
            }
            break;
        }
// 借书
        case 11: {
            system("cls");
            int bookId;
            int userId;
            printf("input the readerId: \n");
            scanf("%d", &userId);
            resultptr res = searchBTree(rdrt, userId);
            if(res->tag == 0) {
                printf("not find the reader(id is %d)\n", userId);
                break;
            } else {
                readerptr rdp = (readerptr)(res->pt->record[res->i]);
                free(res);
                flush(stdin);
                printf("input bookId. until enter [ctrl+z] to complete \n");
                while(~scanf("%d", &bookId)) {
                    resultptr result = searchBTree(t, bookId);
                    if(result->tag == 0) {
                        printf("not find the book(id is %d) \n", bookId);
                    } else {
                        if(borrowBook((bookptr)(result->pt->record[result->i]), rdp)) {
                            time_t tm = time(NULL);
                            char time[25];
                            strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&tm));
                            char *msg = catchString(6, time, "      Borrow ", ((bookptr)(result->pt->record[result->i]))->name, "    readerId : ", int2str(userId),"\n");
                            writeLog(msg);
                            free(msg);
                            printf("borrow book successfully \n");
                        } else {
                            printf("borrow book failed \n");
                        }
                    }
                    free(result);
                    printf("input bookId. until enter [ctrl+z] to complete \n");
                }
            }
            save(t, rdrt);
            break;
        }
// 还书
        case 12: {
            system("cls");
            int userId;
            printf("input the readerId:\n");
            scanf("%d", &userId);
            printf("input bookId. until enter [ctrl+z] to complete \n");
            int bookId;
            int cnt;
            while(~(cnt = scanf("%d", &bookId))) {
                if(!cnt) {
                    flush();
                    printf("illegal input \n");
                    continue;
                }
                resultptr result = searchBTree(t, bookId);
                if(result->tag == 0) {
                    printf("not find the book(id is %d) \n", bookId);
                } else {
                    if(returnBook((bookptr)(result->pt->record[result->i]), userId)) {
                        printf("return book successfully \n");
                        time_t tm = time(NULL);
                        char time[25];
                        strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&tm));
                        char *msg = catchString(6, time, "      Return ", ((bookptr)(result->pt->record[result->i]) )->name, "    readerId : ", int2str(userId),"\n");
                        writeLog(msg);
                        free(msg);
                    }
                }
                printf("input bookId. until enter [ctrl+z] to complete \n");
            }
            save(t, rdrt);
            break;
        }
// 退出
        case 13:
            return 0;
// input overflow option range
        default: {
            system("cls");
            printf("\n\n\n\n\n||->>                         illegal command! [enter] to continue to input order! \n");
            break;
        }
        }
        flush();
        getchar();
        system("cls");
        menu();
        scanf("%d", &option);
    }
    getchar();
    return 0;
}

♻️ 资源

在这里插入图片描述

大小: 2.04MB
➡️ 资源下载:https://download.csdn.net/download/s1t16/87430274
注:如当前文章或代码侵犯了您的权益,请私信作者删除!