在进入正题之前我们先来了解一下单链表的实现以头插法为例子,这里我采用伪代码加图解的方式进行说明:
废话不多说先上图:
来一段伪代码吧,本来不想写的......,emm这里的代码与下面实现的功能不相关,只能是说理解单链表的功能吧。将就着看吧,我懒得再去打开编译器了,就直接在这上面写了,如果有错的地方自己改一改。
#include
#include
struct node{
int n;
struct node *next;
};
void insert_node(struct node* head,int n);
void d_node(struct node* head,int i);
void get_precursor(struct node* head,struct node* delete_node,struct node* precursor);
void printf_list(struct node* head);
void main(){
//初始化头节点
struct node* head;
struct node delete_node;
int i;
head = (struct node*)malloc(sizeof(struct node));
head->next = NULL;
for(i=1;i<10;i++){
insert_node(head,i);
}
printf_list(head);
d_node(head,5);
printf_list(head);
system("pause");
}
//插入新节点
void insert_node(struct node* head,int n){
struct node* new_node;
new_node= (struct node*)malloc(sizeof(struct node));
new_node->n = n;
//按照上图的方式,往链表中插入新节点的指向指向头节点的指向,头节点指向新节
new_node->next = head->next;
head->next=new_node;
}
//删除节点
void d_node(struct node* head,int i){
struct node *p = head;
struct node *del = head->next->next;
int j;
//按照上图的方式
//1、判断是否为**个节点,如果是**个节点就不需要去找他的前驱节点,因为他的前驱节点就是head
if(del->next == NULL){
//直接释放
head->next = NULL;
free(del);
return;
}
//删除节点的前一个节点
for(j = 1;j < i;j++){
p = p->next;
}
del = p ->next;
p->next = p->next->next; //指向删除节点的指向
free(del);
}
void printf_list(struct node* head){
struct node* p;
p = head->next;
while(p){
printf("%d\t",p->n);
p = p->next;
}
printf("\n");
}
1、图书管理存在以下功能:
附上地址:
该系统采用配置文件的方式进行管理存储的数据:
为什么要添加这个配置文件呢?因为不同结构体的大小的不一样的,去读取文件信息的时候不能只用一个结构体去读取数据。如果录入信息的时候按照书籍信息,学生信息,借阅记录的顺序存入文件,这样做会存在一个问题当你想再次插入一个书籍信息的时候,只能将书籍信息后面的数据拷贝到临时文件中当插入完成的时候在将临时文件追加到文件的末尾。
于是我就采用了配置文件的方式,这个文件保存了录入信息时的类型及个数,这样就解决了录入信息时存储的链表只需要追加到文件末尾不需要拷贝临时文件。
录入书籍信息与录入学生信息的实现逻辑:
1、初始化链表,输入书籍信息,校验书籍信息是否存在链表中,校验书籍信息是否存在文件中,如果书籍信息还没有录入则将新节点插入到链表中,写入到文件系统,释放链表内存。
2、初始化链表,输入学生信息,校验学生信息是否存在链表中,校验学生信息是否存在文件中,如果学生信息还没有录入则将新节点插入到链表中,写入到文件系统,释放链表内存。
部分代码:
/**
* 录入书籍信息
*/
void input_books(struct book* head,char *file_path,char *config_path){
char ch;
while(1){
input_book(head,file_path,config_path);
printf("是否继续录入(Y/N):");
//无缓存区,有回显
ch = getche();
printf("\n");
if(ch != 'Y'){
//将当前链表写入文件
write_book(head,file_path);
//写入配置文件
write_manage_info(BOOK_TYPE,get_book_list_size(head),config_path);
//printf_config_info(config_path);
//清空当前链表
clear_book_list(head);
break;
}
}
}
/*
* 录入学生信息
*/
void input_students(struct student_info* head,char *file_path,char *config_path){
char ch;
while(1){
input_student(head,file_path,config_path);
printf("是否继续录入(Y/N):");
//无缓存区,有回显
ch = getche();
printf("\n");
if(ch != 'Y'){
//将当前链表写入文件
write_student(head,file_path);
//写入配置文件
write_manage_info(STUDENT_TYPE,get_student_list_size(head),config_path);
//printf_config_info(config_path);
//清空当前链表
clear_student_list(head);
break;
}
}
创建图书信息:
/**
创建图书信息
*/
void input_book(struct book *head,char *file_path,char *config_path){
struct book *new_book;
struct book fb;
new_book = (struct book*)malloc(sizeof(struct book));
printf("请输入图书编号:");
scanf("%s",new_book->no);
//已经存在
if(find_book(head,new_book->no) == 0 || find_book_file(file_path,config_path,new_book->no,&fb) == 0){
free(new_book);
printf("图书已经录入!\n");
return;
}
printf("请输入书名:");
scanf("%s",new_book->book_name);
printf("请输入作者:");
scanf("%s",new_book->author);
printf("请输入库存:");
scanf("%d",&new_book->reserve);
new_book->next = head->next;
head->next = new_book;
}
/**
* 将图书信息写入到文件中
* 完成创建的时候写入文件
*/
void write_book(struct book *head,char *file_path){
struct book *p;
FILE *fp;
p = head->next;
fp = fopen(file_path,"ab+");
if(!fp){
printf("文件打开失败!\n");
return;
}
while(p != NULL){
fwrite(p,sizeof(struct book),1,fp);
p = p->next;
}
fclose(fp);
}
查看书籍、学生、借阅记录信息:
实现逻辑,首先读取配置文件,配置文件保存了录入书籍,学生,借阅记录等重要信息。
这里没有读取数据到链表中是因为,如果数据量过大的话会耗费很多内存所以就没读取到链表里面。
/*
* 打印数据信息
* @param file_path 查找文件的路径
* @param config_path 配置文件路径
* @param operate_type 操作类型 0 书籍,1学生,2借阅信息
*/
int printf_file(char *file_path,char *config_path,int operate_type){
FILE *fp; //存储数据的文件指针
FILE *cfp; //存储配置文件的文件指针
int i;
struct book_manage node;
struct book book;
struct student_info su_info;
struct borrowing_records br;
int is_first = 0;
fp = fopen(file_path,"rb");
cfp = fopen(config_path,"rb");
if(!cfp || !fp){
printf("文件打开失败!\n");
return 2;
}
printf("\n");
printf("==============================================================\n");
//读取配置信息
while((fread(&node,sizeof(struct book_manage),1,cfp)) == 1){
//书籍信息
if(node.type == BOOK_TYPE){
if(node.type == operate_type){
for(i = 0;i
借阅书籍实现:
首先输入学生学号,校验是否存在该学生,如果学生信息存在,输入书籍编号,校验书籍信息是否存在,如果存在校验当前书籍是否还可以借阅,如果可以书籍信息可借阅的数量减1,将修改过后的书籍信息覆盖原先的书籍信息重新写入,*后写入一条借阅记录,先查找是否有标记为”-1“的空间,存在则在当前位置写入数据,不存在则追加在文件末尾。
void borrow_books(char *file_path,char *config_path){
struct book find_book;
struct borrowing_records *br;
struct student_info su_info;
br = (struct borrowing_records*) malloc(sizeof(struct borrowing_records));
printf("请输入学生学号:");
scanf("%s",br->student_id);
if(find_student_file(file_path,config_path,br->student_id,&su_info) != 0){
printf("学生信息不存在\n");
return;
}
printf("请输入借阅书籍编号:");
scanf("%s",br->no);
if(find_book_file(file_path,config_path,br->no,&find_book) != 0){
printf("书籍信息不存在\n");
return;
}
if(find_book.reserve == 0){
printf("没有图书可借阅,请过段时间再来\n");
return;
}
//写入借阅记录
write_borrow_books_count(file_path,br);
write_manage_info(BORROWING_RECORDS_TYPE,1,config_path);
//修改图书信息
find_book.reserve--;
change_book(file_path,config_path,&find_book);
printf("借阅成功!\n");
}
//修改书籍信息
void change_book(char *file_path,char *config_path,struct book *change_book){
//1、查找到当前book文件位置
FILE *fp; //存储数据的文件指针
FILE *cfp; //存储配置文件的文件指针
int i;
struct book_manage node;
struct book book;
long book_size;
book_size = sizeof(struct book);
fp = fopen(file_path,"rb+");
cfp = fopen(config_path,"rb");
if(!cfp || !fp){
printf("文件打开失败!\n");
}
//读取配置信息
while((fread(&node,sizeof(struct book_manage),1,cfp)) == 1){
//书籍信息
if(node.type == BOOK_TYPE){
for(i = 0;i no) == 0){ //存在书籍信息
fseek(fp,-book_size,SEEK_CUR);
fwrite(change_book,book_size,1,fp);
fclose(fp);
fclose(cfp);
return;
}
}
continue;
}else if(node.type == STUDENT_TYPE){
//文件指针移动
for(i = 0;ino,"-1") == 0 || strcmp(br->student_id,"-1") == 0){
fseek(fp,-br_size,SEEK_CUR);
fwrite(br,br_size,1,fp);
fclose(fp);
fclose(cfp);
return;
}
}
}
}
fwrite(br,br_size,1,fp);
fclose(fp);
fclose(cfp);
}
归还书籍的实现:
首先校验学生信息是否存在,再校验是否存在书籍信息,然后再校验是否存在借阅记录,如果不存在说明没有借阅过书籍,存在修改借阅记录将借阅记录重新赋值为”-1“当作标记为空闲空间,当写入借阅记录时优先写入避免存储空间浪费,*后修改书籍信息可借阅的数量加1,将修改过后的书籍信息覆盖原先的书籍信息重新写入。
void return_the_book(char *file_path,char *config_path){
struct book find_book;
struct borrowing_records *br;
struct borrowing_records find_br;
struct student_info su_info;
br = (struct borrowing_records*) malloc(sizeof(struct borrowing_records));
printf("请输入学生学号:");
scanf("%s",br->student_id);
if(find_student_file(file_path,config_path,br->student_id,&su_info) != 0){
printf("学生信息不存在\n");
return;
}
printf("请输入归还书籍编号:");
scanf("%s",br->no);
if(find_book_file(file_path,config_path,br->no,&find_book) != 0){
printf("书籍信息不存在\n");
return;
}
//查找借阅记录
if(find_borrowing_records_file(file_path,config_path,br,&find_br) != 0){
printf("不存在借阅记录\n");
return;
}
//修改借阅记录
change_borrow_books(file_path,config_path,&find_br);
//修改图书信息
find_book.reserve++;
change_book(file_path,config_path,&find_book);
}
//修改借阅记录
void change_borrow_books(char *file_path,char *config_path,struct borrowing_records *br){
//1、查找到当前借阅记录文件位置
FILE *fp; //存储数据的文件指针
FILE *cfp; //存储配置文件的文件指针
int i;
struct book_manage node;
struct borrowing_records borrowing_record;
long br_size;
br_size = sizeof(struct borrowing_records);
fp = fopen(file_path,"rb+");
cfp = fopen(config_path,"rb");
if(!cfp || !fp){
printf("文件打开失败!\n");
}
//读取配置信息
while((fread(&node,sizeof(struct book_manage),1,cfp)) == 1){
//书籍信息
if(node.type == BOOK_TYPE){
for(i = 0;ino,borrowing_record.no) == 0 && strcmp(br->student_id,borrowing_record.student_id) == 0){ //存在借阅信息
fseek(fp,-br_size,SEEK_CUR);
//清空文件,后面复用
strcpy(br->no,"-1");
strcpy(br->student_id,"-1");
fwrite(br,br_size,1,fp);
fclose(fp);
fclose(cfp);
return;
}
}
}
}
fclose(fp);
fclose(cfp);
}
部分运行截图: