用C语言,编写一个简陋的链表形式的图书管理系统

一、前情介绍

       好久不见,甚是想念,博主很久没有写东西了,刚好博主完成了答辩,今天博主就贡献出自己第一次期末答辩的作品,希望为以后的同学们提供一些灵感。

       首先,我先描述一下,产品:

        1.这是一个图书馆信息管理系统,有两级菜单,一级菜单是主菜单,二级菜单是黑名单

        2.主菜单具有以下功能:录入,查询,修改,展示,黑名单(二级菜单入口),退出等六项功能。

        3.黑名单具有以下功能:录入,查询,修改,删除,展示,退出等六项功能。

        4.不管是正常录入功能,还是黑名单录入功能,都具有查重功能,并且正常的录入功能还具有核查录入人员信息是否已经在黑名单中,在的话,将重新输入。

        5.本系统,设置了两条链表,一条是正常菜单链表,一条是黑名单链表

        6.本系统还设有登录系统,需输入密码才能进入

        7.为方便测试两条链表都设有实验数据

        8.本系统设有颜色模块,可以从代码上提前设定字体颜色。

        因为这是第一次编写链表形式的程序(虽然起他程序也没有打过多少),所以就比较简陋,功能上比较残缺,希望大家多多见谅。

二、系统组成文件介绍

        接下来由我为大家一一介绍我的系统组成。

2.1main.c文件

#define _CRT_SECURE_NO_WARNINGS 1//编译环境自带
#include<stdio.h>
#include <stdlib.h>
#include"menu.h"
#include"denglu.h"
#include"gongleng.h"
#include"heimingdan.h"
#include"color.h"

int main()
{
	if (!login())//如果登录失败
	{
		set_text_color(COLOR_RED);//赋予颜色
		printf("再见!\n");
		set_text_color(COLOR_RESET); // 重置颜色
		return 0;
	}
	library_shuju();//实验数据
	
	int choose = -1;
	while (choose)
	{
		libreary_menu1();
		scanf("%d",&choose);
		switch (choose)
		{
		case 1:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("查询\n");
			library_chaxun();
			break;
		case 2:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("录入\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_luru();
			break;
		case 3:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("修改\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_xiugai();
			break;
		case 4:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("展示\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_zhanshi();//展示
			break;
		case 5:
			set_text_color(COLOR_MAGENTA);//赋予颜色
			printf("黑名单\n");
			system("pause");
			system("cls");
			set_text_color(COLOR_RESET); // 重置颜色
			library_heimingdan();
			break;
		case 6:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("退出\n");
			return 0;
			set_text_color(COLOR_RESET); // 重置颜色
		default:
			set_text_color(COLOR_YELLOW);//赋予颜色
			printf("没有其他选项哦!\n");
			system("pause");
			system("cls");
			set_text_color(COLOR_RESET); // 重置颜色
			break;

		}
	}


		return 0;
}

       以上便是main.c文件,在这个文件中由头文件,主函数,while循环语句,switch分支语句,调用函数等内容组成。它通过while循环,实现了重复进行功能的选择,switch语句提供了选择的分支,在其中通过调用提前编写好的的各个功能的自定义函数的函数名,实现选择的功能。文件最上方的一大列头文件保证了调用的函数正常运行,可以跨文件使用。

2.2menu文件

      下图是装菜单的menu.c文件的内容,大家可能会问,平时不都是一个main.c文件吗?今天怎么就这么多文件?其实把程序分成多个文件,其实是为了方便调用,看起来也整齐,使用方便,这么个做法也叫做模块化封装程序。每一个.c文件都会配上一个.h文件,在main.c文件中,.h文件就可以当头文件,为你调用你编写的函数做声明,保证它可以跨文件使用。

回归正题,接下来是menu.c文件

#define _CRT_SECURE_NO_WARNINGS 1//编译环境自带
#include<stdio.h>
#include"color.h"


void libreary_menu1(void)
{
	set_text_color(COLOR_GREEN);//赋予颜色
	printf("欢迎您的到来\n");
	printf("图书馆信息管理系统为您服务!\n");
	set_text_color(COLOR_RESET); // 重置颜色
	set_text_color(COLOR_YELLOW);//赋予颜色
	printf("1.查询\n");
	printf("2.录入\n");
	printf("3.修改\n");
	printf("4.展示\n");
	printf("5.黑名单\n");
	printf("6.退出\n");
	set_text_color(COLOR_RESET); // 重置颜色
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入您的选择:");
	set_text_color(COLOR_RESET); // 重置颜色
}

void libreary_menu2(void)
{
	set_text_color(COLOR_GREEN);//赋予颜色
	printf("欢迎您的到来\n");
	printf("图书馆黑名单信息管理系统为您服务!\n");
	set_text_color(COLOR_RESET); // 重置颜色
	set_text_color(COLOR_YELLOW);//赋予颜色
	printf("1.查询\n");
	printf("2.录入\n");
	printf("3.修改\n");
	printf("4.展示\n");
	printf("5.删除\n");
	printf("6.退出\n");
	set_text_color(COLOR_RESET); // 重置颜色
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入您的选择:");
	set_text_color(COLOR_RESET); // 重置颜色
}

以上便是会调用到的两个菜单函数,它们会打印出我们看到的菜单页面,接下来是它的.h文件,menu.h。

#pragma once//编译环境自带
#ifndef MENU_H//固定格式
#define MENU_H//固定格式
void libreary_menu1();
void libreary_menu2(void);//void可要可不要,因为在这里只是声明,并不影响运行


#endif//固定格式

      看着以上的.h文件是不是感觉它的内容少了好多?是不是想问为什么?少就对了!因为.h文件就是为了声明的,这里是拿来装.c文件中你所编写的函数的函数名的。当你在主函数中调用到了.c文件中的函数,那么你就需要将.h文件当作头文件,放到最上面声明,并且自己写的.h文件,与库函数的头文件的写法是不一样的。

#include<stdio.h>//库函数头文件
#include"color.h"//自定义函数头文件

相信大家都看得出来,库函数的头文件加的是书名号而自定义函数的头文件加的则是双引号。 

接下来我们看看调用menu.c文件中函数的效果的。

1.void libreary_menu1();

2. void libreary_menu2(void);

       可以看到上面的图片,“请输入您的选择:”这句话是 主函数中设定好的,可不是博主失误了,当然失误了也不是这个样子的。

2.3denglu文件

      下一个我们就近来看看登录函数吧,denglu.c文件,它会创造一个登录界面,并且给你三次输入密码的机会。

#define _CRT_SECURE_NO_WARNINGS 1//编译环境自带
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include <stdlib.h>
#include"color.h"
_Bool login()//登录函数
{
	set_text_color(COLOR_GREEN);//赋予颜色
	printf("**************************\n");
	printf("*请登录图书馆信息管理系统*\n");
	printf("**************************\n");
	set_text_color(COLOR_RESET); // 重置颜色

	const char* username = "w";
	const char* password = "1";
	char name[10];
	char word[10];
	int se = 0;
	while (se < 3)//限制输入次数
	{
		set_text_color(COLOR_CYAN);//赋予颜色
		printf("请输入用户名:");
		scanf("%s", &name);
		printf("请输入密码:");
		scanf("%s", &word);
		set_text_color(COLOR_RESET); // 重置颜色
		if (strcmp(username, name) == 0 && strcmp(password, word) == 0)
		{
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("登陆成功!\n");
			system("pause");
			system("cls");
			set_text_color(COLOR_RESET); // 重置颜色
			return true;
		}
		else
		{
			se++;
			set_text_color(COLOR_YELLOW);//赋予颜色
			printf("用户或者密码错误,请再次尝试,剩余%d次!\n", 3 - se);
			set_text_color(COLOR_RESET); // 重置颜色
		}
	}
	set_text_color(COLOR_RED);//赋予颜色
	printf("登陆尝试次数超过上限!\n");
	set_text_color(COLOR_RESET); // 重置颜色
	return false;
}

       相信大家应该已经注意到了,其实这些封装的.c文件,在编写属于自己的函数时,一样需要带有头文件,因为没有头文件,在你的函数中所使用的库函数可就发挥不了作用了。

const char* username = "sdfgm";//用户名
    const char* password = "asd1111";//密码

这就是我提前设置好的用户名和密码,担心大家看不懂,咱就给大家单拎出来提一下。

if (strcmp(username, name) == 0 && strcmp(password, word) == 0)

        以上是一段对比的表达式只有满足它,才能进入if语句内,不过咱们的重点是表达式,中的函数,strcmp,这个函数呢,它有着比较大小的功能,咱们也不提太多原理,咱们只需要知道,strcmp这个库函数,可以比较大小,两个字符串,str1,str2,如果str1>str2,则返回值大于0,相反则小于0,相等的话就会返回0,在这里咱们只需要它返回零的特点,来看看我们输入的用户名和密码是否正确。相信剩下的大家配合注释应该能看懂,那么

咱们在放出它的.h文件,denglu.h。

#pragma once//编译环境自带
#ifndef DENGLU_H//固定格式
#define DENGLU_H//固定格式
_Bool login();



#endif//固定格式

 下面是调用它的效果图;

2.4gongleng文件 

       下图是功能实现的.c文件,所有的分支功能,小功能,我都装在在这里面哦!

        gongleng.c文件

#define _CRT_SECURE_NO_WARNINGS 1//编译环境自带
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include"gongleng.h"
#include"color.h"
typedef struct xinxi xi;//全局变量,用于存放学生成绩信息
typedef struct heimingdanrenyuan hmy;//全局变量,用于存放学生成绩信息
xinxi* head = NULL;
renan* head1 = NULL;
void library_shuju(void)
{
	xinxi* p = head;//用来声明一个指向xinxi结构体的指针p,并将其初始化为头节点
	xinxi* prev = NULL;//用来声明一个指向xinxi结构体的指针prev,用于记录遍历过程中的前一个地址

	// 创建第一个节点
	// 分配内存空间给一个新的xinxi结构体实例
	xinxi* newNode1 = (xinxi*)malloc(sizeof(xinxi));
	newNode1->stunum = 1;
	strcpy(newNode1->name, "林永权");
	newNode1->sex = 'M';
	strcpy(newNode1->time, "2024年6月12日");
	strcpy(newNode1->arr, "14223542637");
	// 将新节点设置为链表的头节点
	head = newNode1;
	// 更新辅助指针p指向当前节点
	p = newNode1;


	// 创建第二个节点
	// 同上,分配内存空间给一个新的xinxi结构体实例
	xinxi*newNode2 = (xinxi*)malloc(sizeof(xinxi));
	newNode2->stunum = 4;
	strcpy(newNode2->name, "岳昱宁");
	newNode2->sex = 'M';
	strcpy(newNode2->time, "2024年6月13日");
	strcpy(newNode2->arr, "14225542637");
	p->pNext = newNode2;
	// 更新辅助指针p指向当前节点,此时p指向链表的最后一个节点
	p = newNode2;

	// 创建第三个节点
	// 同上,分配内存空间给一个新的xinxi结构体实例
	xinxi* newNode3 = (xinxi*)malloc(sizeof(xinxi));
	newNode3->stunum = 6;
	strcpy(newNode3->name, "林宁");
	newNode3->sex = 'M';
	strcpy(newNode3->time, "2024年6月13日");
	strcpy(newNode3->arr, "14225542637");
	p->pNext = newNode3;
	// 更新辅助指针p指向当前节点,此时p指向链表的最后一个节点
	p = newNode3;
	p->pNext = NULL;
}

int duibi(renan* he, xinxi* hr) 
{
	renan* an = he;
	xinxi* am = hr;

	while (an != NULL && am != NULL) 
	{
		if (strcmp(an->name, am->name) == 0) 
		{
			return 1; // 姓名匹配,返回 1
		}
		an = an->pNext;
		//am = am->pNext;
	}

	return 0; // 姓名不匹配,返回 0
}

int duibi2(xinxi* head, int stunum, char* time)
{
	xinxi* p = head;
	while (p != NULL) 
	{
		if (p->stunum == stunum && strcmp(p->time, time) == 0)
		{
			return 1; // 书籍已被借阅
		}
		p = p->pNext;
	}
	return 0; // 书籍未被借阅
}

// 检查黑名单人员编号是否已存在
int duibi3(renan* head1, int stunum)
{
	renan* q = head1;
	while (q != NULL) 
	{
		if (q->stunum == stunum)
		{
			return 1; // 编号已存在
		}
		q = q->pNext;
	}
	return 0; // 编号不存在
}

// 检查黑名单人员姓名和电话号码是否已存在
int duibi4(renan* head1, char* name, char* ard)
{
	renan* q = head1;
	while (q != NULL) 
	{
		if (strcmp(q->name, name) == 0 && strcmp(q->ard, ard) == 0)
		{
			return 1; // 姓名和电话号码已存在
		}
		q = q->pNext;
	}
	return 0; // 姓名和电话号码不存在
}

//正常图书馆功能
void library_luru(void)//录入
{
	xinxi* p = head;//用来声明一个指向xinxi结构体的指针p,并将其初始化为头节点
	xinxi* prev = NULL;//用来声明一个指向xinxi*结构体的指针prev,用于记录遍历过程中的前一个地址

	xinxi* newNode = (xinxi*)malloc(sizeof(xinxi));//malloc动态分配空间


	renan* q = head1;//用来声明一个指向renan*结构体的指针q,并将其初始化为头节点
	renan* prev1 = NULL;//用来声明一个指向renan*结构体的指针prev1,用于记录遍历过程中的前一个地址
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅书籍的编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &newNode->stunum);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅人的姓名:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode->name);
	getchar();//清除缓冲区分隔符
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅人的性别(男生M或女生F):");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%c", &newNode->sex);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅时间:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode->time);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅人的电话号码:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode->arr);
	
	newNode->pNext = NULL;
	
	if (duibi(q, newNode)) //调用函数
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("检测到%s是黑名单人员,不予录入!请重新输入\n", newNode->name);
		free(newNode); // 释放 newNode 所占用的内存空间
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return;

	}
	
	if (duibi2(head, newNode->stunum, newNode->time)) 
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("该书籍编号已被%s在%s借阅,不能重复借阅!\n", newNode->name, newNode->time);
		free(newNode);
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return;
	}


		if (p == NULL)
		{
			set_text_color(COLOR_GREEN);//赋予颜色
			head = newNode;
			printf("head is null\n");
			set_text_color(COLOR_RESET); // 重置颜色


		}
		else if (p!= NULL && newNode->stunum < p->stunum)//新输入的数据比头节点小,且头节点不为空
		{
			newNode->pNext = p;//指向比它大的节点
			head = newNode;//将头节点开辟的空间拿来放新节点,新结点的空间用来当临时空间

		}
		
		else
		{
			while (p!= NULL && newNode->stunum > p->stunum)
			{
				prev = p;//记录新节点前面那个小于他的结点的地址
				p = p->pNext;//将地址换成下一个,继续比较
			}
			if (prev == NULL)//当 prev =null时,已经找遍了整条链表,所以新节点放在最后
			{
				// 如果 prev 为 NULL,说明新节点应该插入到链表当前的头节点
				newNode->pNext = head;
				head = newNode;
			}
			else//当找到了不比新节点小的记录,就把节点插在前面。
			{
				prev->pNext = newNode;//之前比新节点小的节点指向新节点
				newNode->pNext = p;//再将新节点指向头结点中的空。直到下一次录入
			}
		}
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("录入学生成绩成功!\n");
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

void library_chaxun(void)//查询
{
	int num;
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要查找的书籍编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	xinxi* temp = head;
	int found = 0;
	while (temp != NULL)//头指针内不为空
	{
		if (temp->stunum == num)
		{
			
			found++ ; // 找到匹配的书籍编号
			set_text_color(COLOR_MAGENTA);//赋予颜色
			printf("找到对应的信息如下:\n");
			printf("编号:%d,姓名:%s,性别:%c,借阅时间:%s,电话号码:%s\n", temp->stunum, temp->name, temp->sex, temp->time, temp->arr);
			set_text_color(COLOR_RESET); // 重置颜色
		}
		temp = temp->pNext;
	}
	if (!found)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("没有匹配学号:\n");
		set_text_color(COLOR_RESET); // 重置颜色

	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}



void library_xiugai(void)//修改
{
	int num;
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要修改的书籍编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	xinxi* temp = head;
	while (temp != NULL)//头指针内不为空
	{
		if (temp->stunum == num)
		{
			break;//跳出循环(筛选)
		}
		temp = temp->pNext;//到下一个节点
	}
	if (temp == NULL)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("没有匹配学号:\n");
		set_text_color(COLOR_RESET); // 重置颜色
	}
	else
	{
		set_text_color(COLOR_CYAN);//赋予颜色
		printf("找到对应的信息如下:\n");
		printf("编号:%d,姓名:%s,性别:%c,借阅时间:%s,电话号码:%s\n", temp->stunum, temp->name, temp->sex, temp->time, temp->arr);
		printf("是否要修改成绩?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();//清空输入缓冲区,以正确的读取输入用户输入的单个字符
		char choose = getchar();
		if (choose == 'y' || choose == 'Y')
		{
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("请输入新的借阅时间:");
			set_text_color(COLOR_RESET); // 重置颜色
			scanf("%s", &temp->time);
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("请输入借阅人的新电话号码:");
			set_text_color(COLOR_RESET); // 重置颜色
			scanf("%s", &temp->arr);
		}


	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

void library_zhanshi(void)//展示
{
	set_text_color(COLOR_CYAN);//赋予颜色
	//打印表头
	printf("编号\t\t姓名\t性别\t借阅时间\t\t电话号码\n");
	set_text_color(COLOR_RESET); // 重置颜色
	xinxi* p = head;//遍历链表,直至p指向为NULL
	while (p != NULL)
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("%-16d%-8s%-8c%-24s%s\n", p->stunum, p->name, p->sex, p->time, p->arr);
		set_text_color(COLOR_RESET); // 重置颜色
		p = p->pNext;//换下一个地址
	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

//黑名单版本功能

void library_shuju2(void)
{
	renan* q = head1;//用来声明一个指向renan结构体的指针q,并将其初始化为头节点
	renan* prev1 = NULL;//用来声明一个指向renan结构体的指针prev1,用于记录遍历过程中的前一个地址
	// 创建第一个节点
	// 分配内存空间给一个新的renan结构体实例
	renan* newNode1 = (renan*)malloc(sizeof(renan));//malloc动态分配空间
	newNode1->stunum = 1;
	strcpy(newNode1->name, "林权");
	newNode1->sex1 = 'M';
	strcpy(newNode1->yuan, "2024年6月12日");
	strcpy(newNode1->ard, "14223542637");
	// 将新节点设置为链表的头节点
	head1= newNode1;
	// 更新辅助指针q指向当前节点
	q = newNode1;


	// 创建第二个节点
	// 同上,分配内存空间给一个新的renan结构体实例
	renan* newNode2 = (renan*)malloc(sizeof(renan));//malloc动态分配空间
	newNode2->stunum = 4;
	strcpy(newNode2->name, "岳宁");
	newNode2->sex1 = 'M';
	strcpy(newNode2->yuan, "2024年6月13日");
	strcpy(newNode2->ard, "14225542637");
	q->pNext = newNode2;
	// 更新辅助指针q指向当前节点,此时q指向链表的最后一个节点
	q = newNode2;

	q->pNext = NULL;
}

void library_luru2(void)//录入
{
	renan* q = head1;//用来声明一个指向renan结构体的指针q,并将其初始化为头节点
	renan* prev1 = NULL;//用来声明一个指向renan结构体的指针prev1,用于记录遍历过程中的前一个地址
	renan* newNode1 = (renan*)malloc(sizeof(renan));//malloc动态分配空间
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入黑名单人员的编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &newNode1->stunum);
	// 查重:检查新录入的人员编号是否已存在
	if (duibi3(head1, newNode1->stunum)) 
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("该编号已存在于黑名单中,请重新输入!\n");
		free(newNode1); // 释放新节点的内存
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return; // 退出函数,不进行录入
	}
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入黑名单人员的姓名:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode1->name);
	getchar();//清除缓冲区分隔符
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入黑名单人员的性别(男生M或女生F):");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%c", &newNode1->sex1);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入进入黑名单的原因:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode1->yuan);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入黑名单人员的电话号码:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode1->ard);
	newNode1->pNext = NULL;
	// 新增:检查姓名和电话号码是否已存在
	if (duibi4(head1, newNode1->name, newNode1->ard)) 
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("该人员已存在于黑名单中,请重新输入!\n");
		free(newNode1); // 释放新节点的内存
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return; // 退出函数,不进行录入
	}


	if (q == NULL)
	{
		set_text_color(COLOR_GREEN);//赋予颜色
		head1 = newNode1;
		printf("head is null\n");
		set_text_color(COLOR_RESET); // 重置颜色

	}
	else if (q != NULL && newNode1->stunum < q->stunum)//新输入的数据比头节点小,且头节点不为空
	{
		newNode1->pNext = q;//指向比它大的节点
		head = newNode1;//将头节点开辟的空间拿来放新节点,新结点的空间用来当临时空间

	}
	

	else
	{
		
		while (q != NULL && newNode1->stunum > q->stunum)
		{
			prev1 = q;//记录新节点前面那个小于他的结点的地址
			q = q->pNext;//将地址换成下一个,继续比较
		}
		

		prev1->pNext = newNode1;//之前比新节点小的节点指向新节点
		newNode1->pNext = q;//再将新节点指向头结点中的空。直到下一次录入
	}
	set_text_color(COLOR_GREEN);//赋予颜色
	printf("录入黑名单人员成功!\n");
	set_text_color(COLOR_RESET); // 重置颜色
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

void library_chaxun2(void)//查询
{
	int num;
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要查询的黑名单人员:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	renan* temp = head1;
	while (temp != NULL)//头指针内不为空
	{
		if (temp->stunum == num)
		{
			break;
		}
		temp = temp->pNext;
	}
	if (temp == NULL)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("没有匹配人员:\n");
		set_text_color(COLOR_RESET); // 重置颜色
	}
	else
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("找到对应的信息如下:\n");
		printf("姓名:%s,性别:%c,原因:%s,电话号码:%s\n", temp->name, temp->sex1, temp->yuan, temp->ard);
		set_text_color(COLOR_RESET); // 重置颜色
	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

void library_xiugai2(void)//修改黑名单人员信息
{
	int num;
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要修改的黑名单人员:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	renan* temp = head1;
	while (temp != NULL)//头指针内不为空
	{
		if (temp->stunum == num)
		{
			break;//跳出循环(筛选)
		}
		temp = temp->pNext;//到下一个节点
	}
	if (temp == NULL)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("没有匹配人员:\n");
		set_text_color(COLOR_RESET); // 重置颜色
	}
	else
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("找到对应的信息如下:\n");
		printf("编号:%d,姓名:%s,性别:%c,原因:%s,电话号码:%s\n", temp->stunum, temp->name, temp->sex1, temp->yuan, temp->ard);
		printf("是否要修改黑名单人员信息?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();//清空输入缓冲区,以正确的读取输入用户输入的单个字符
		char choose = getchar();
		if (choose == 'y' || choose == 'Y')
		{
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("请输入新的黑名单人员姓名:");
			set_text_color(COLOR_RESET); // 重置颜色
			scanf("%s", &temp->name);
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("请输入黑名单人员的新电话号码:");
			set_text_color(COLOR_RESET); // 重置颜色
			scanf("%s", &temp->ard);
		}
		set_text_color(COLOR_CYAN);//赋予颜色
		printf("修改完成!");
		set_text_color(COLOR_RESET); // 重置颜色

	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

void library_zhanshi2(void)//展示
{
	set_text_color(COLOR_CYAN);//赋予颜色
	//打印表头
	printf("编号\t\t姓名\t性别\t原因\t\t\t电话号码\n");
	set_text_color(COLOR_RESET); // 重置颜色
	renan* q = head1;//遍历链表,直至p指向为NULL
	while (q != NULL)
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("%-16d%-8s%-8c%-24s%s\n", q->stunum, q->name, q->sex1, q->yuan, q->ard);
		q =q->pNext;//换下一个地址
		set_text_color(COLOR_RESET); // 重置颜色
	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}


void library_shanchu(void)
{
	int num;
	renan* q = head1;//用来声明一个指向renan结构体的指针p,并将其初始化为头节点
	renan* prev1 = NULL;//用来声明一个指向renan结构体的指针prev1,用于记录遍历过程中的前一个地址
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要删除的黑名单人员编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	if (head1== NULL)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("链表为空,没有数据\n");
		system("pause");
		set_text_color(COLOR_RESET); // 重置颜色
		return;
	}
	if (head1->stunum == num)
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("编号:%d,姓名:%s\n,性别:%c,原因:%s,电话号码:%s \n",q->stunum, q->name,q->sex1, q->yuan,q->ard);
		printf("是否要删除记录?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();清除缓冲区分隔符,避免影响后面字符的读取
		char choose = getchar();//获取用户选择
		if (choose == 'y' || choose == 'Y')
		{
			set_text_color(COLOR_YELLOW);//赋予颜色
			prev1 = q->pNext;
			free(q);//释放源头节点的内存
			head1 = prev1;//将指针指向头节点的一节点
			printf("删除编号%d的黑名单人员成功!\n", num);
			set_text_color(COLOR_RESET); // 重置颜色

		}
		set_text_color(COLOR_CYAN);//赋予颜色
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return;
	}


	while (q != NULL && q->stunum != num)
	{
		prev1 = q;
		q= q->pNext;
	}
	if (q != NULL)
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("找到的信息如下:\n");
		printf("编号:%d,姓名:%s,性别:%c,原因:%s,电话号码:%s\n",q->stunum,q->name, q->sex1, q->yuan,q->ard);
		printf("是否要删除记录?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();清除缓冲区分隔符,避免影响后面字符的读取
		char choose = getchar();//获取用户选择
		if (choose == 'y' || choose == 'Y')
		{
			prev1->pNext = q->pNext;//更新前一个节点的指针,跳过要删除的节点
			free(q);//释放源头节点的内存
			set_text_color(COLOR_GREEN);//赋予颜色
			printf("删除编号%d的黑名单人员成功!\n", num);
			set_text_color(COLOR_RESET); // 重置颜色
		}
		else
		{
			set_text_color(COLOR_YELLOW);//赋予颜色
			printf("编号%d的黑名单人员未找到。\n", num);
			set_text_color(COLOR_RESET); // 重置颜色
		}
	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
	return;
}

       以上便是所有的功能实现了,以为数量有点多,咱们就不全部讲了,就挑几个来讲,没讲的看不懂没关系,博主有空的时候会将相关知识分享出来的。

2.4.1void library_luru()

咱们讲讲录入功能void library_luru(void)//录入

void library_luru(void)//录入
{
	xinxi* p = head;//用来声明一个指向xinxi结构体的指针p,并将其初始化为头节点
	xinxi* prev = NULL;//用来声明一个指向xinxi*结构体的指针prev,用于记录遍历过程中的前一个地址

	xinxi* newNode = (xinxi*)malloc(sizeof(xinxi));//malloc动态分配空间


	renan* q = head1;//用来声明一个指向renan*结构体的指针q,并将其初始化为头节点
	renan* prev1 = NULL;//用来声明一个指向renan*结构体的指针prev1,用于记录遍历过程中的前一个地址
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅书籍的编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &newNode->stunum);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅人的姓名:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode->name);
	getchar();//清除缓冲区分隔符
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅人的性别(男生M或女生F):");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%c", &newNode->sex);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅时间:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode->time);
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入借阅人的电话号码:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%s", &newNode->arr);
	
	newNode->pNext = NULL;
	
	if (duibi(q, newNode)) //调用函数
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("检测到%s是黑名单人员,不予录入!请重新输入\n", newNode->name);
		free(newNode); // 释放 newNode 所占用的内存空间
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return;

	}
	
	if (duibi2(head, newNode->stunum, newNode->time)) 
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("该书籍编号已被%s在%s借阅,不能重复借阅!\n", newNode->name, newNode->time);
		free(newNode);
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return;
	}


		if (p == NULL)
		{
			set_text_color(COLOR_GREEN);//赋予颜色
			head = newNode;
			printf("head is null\n");
			set_text_color(COLOR_RESET); // 重置颜色


		}
		else if (p!= NULL && newNode->stunum < p->stunum)//新输入的数据比头节点小,且头节点不为空
		{
			newNode->pNext = p;//指向比它大的节点
			head = newNode;//将头节点开辟的空间拿来放新节点,新结点的空间用来当临时空间

		}
		
		else
		{
			while (p!= NULL && newNode->stunum > p->stunum)
			{
				prev = p;//记录新节点前面那个小于他的结点的地址
				p = p->pNext;//将地址换成下一个,继续比较
			}
			if (prev == NULL)//当 prev =null时,已经找遍了整条链表,所以新节点放在最后
			{
				// 如果 prev 为 NULL,说明新节点应该插入到链表当前的头节点
				newNode->pNext = head;
				head = newNode;
			}
			else//当找到了不比新节点小的记录,就把节点插在前面。
			{
				prev->pNext = newNode;//之前比新节点小的节点指向新节点
				newNode->pNext = p;//再将新节点指向头结点中的空。直到下一次录入
			}
		}
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("录入学生成绩成功!\n");
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

 录入这个函数由信息录入,核查黑名单,书籍编号查重,编号自动排序等四个功能组成,

首先最简单的是信息录入的实现

printf("请输入借阅书籍的编号:");
    scanf("%d", &newNode->stunum);
    printf("请输入借阅人的姓名:");
    scanf("%s", &newNode->name);
    getchar();//清除缓冲区分隔符
    printf("请输入借阅人的性别(男生M或女生F):");
    scanf("%c", &newNode->sex);
    printf("请输入借阅时间:");
    scanf("%s", &newNode->time);
    printf("请输入借阅人的电话号码:");
    scanf("%s", &newNode->arr);
    
    newNode->pNext = NULL;
 

        通过重复使用printfscanf函数完成了信息录入的功能,我想这并不需要多讲,不过  newNode->pNext = NULL;,同学们可能会好奇它是干嘛的,它其实是结尾的,本系统因为采用的是链表的数据结构,所以它给节点装一个地址,这里赋空值,表示链表结束。

       说到这里咱们就简单提一下什么是链表,链表链表,字如其名,它就像是一条铁链一样的数据结构,或者说储存方式,它是一环扣一环的,用你的左手食指与大拇指掐成环,再用你的右手食指穿环而过,在用右手大拇指掐住右手食指指尖,这就是一个简单的链表。

       简单来说,链表就是由很多个节点做组成的,每一个节点都分为两个部分,你想装的信息数据,简称数据域,还有下一个节点的地址,简称指针域,由这样的节点组成了链表,而铁链有长短,链表自然也有,在链表结束之时就需要给它赋控制,防止它成为野指针(至于什么是野指针,看我往期知识分享,如果没有的话,以后我也会写的)。

       以上也是我为什么会在录入信息完毕之后赋空值的原因,NULL意思是空的,所以我叫它空值

的原因。

      这里的信息便是写入了一个新的节点,每一个节点都是结构体的形式,而地址(指针)的格式是提前写好的,比如以下

typedef struct xinxi//定义结构体,用于存放学生信息
{
    int stunum;//存放书籍编号
    char name[20];//存放借阅人员姓名
    char sex;//存放性别
    char time[30];//借阅时间
    char arr[20];//存放电话号码
    struct xinxi* pNext;//pNxet,是指针类型,指向下一个Student结构体,用于构建链表
}xinxi;

       便是一个结构体,其中struct xinxi* pNext是不是很眼熟,没错newNode->pNext = NULL它也有pNext,这就是存放指针的指针变量名,struct xinxi它表示结构体类型,->至于这个箭头,是一种特有格式,表示指向 struct xinxi中的pNext这个变量的地址,而在数组的数据结构中,则是用“.”。

       好了以上便是信息录入如果有错或不懂的地方都可以直接询问我,我会注意到了的话会尽量回答的。

以下是代码运行结果:

        下面是核查黑名单的功能实现,其实它很简单

if (duibi(q, newNode)) {
        printf("检测到%s是黑名单人员,不予录入!请重新输入\n", newNode->name);
        free(newNode); // 释放 newNode 所占用的内存空间
        system("pause");
        system("cls");
        return;

    }

因为它通过使用if语句在表达式中调用duibi函数实现的两条链表中的姓名的信息对比,如果姓名相同的话就会返回1,成功进入if语句被判断为黑名单人员,让你重新输入。

int duibi(renan* he, xinxi* hr) {
    renan* an = he;
    xinxi* am = hr;

    while (an != NULL && am != NULL)//防止节点中没有信息,

{
        if (strcmp(an->name, am->name) == 0) {
            return 1; // 姓名匹配,返回 1
        }
        an = an->pNext;//换下黑名单一个节点
    }

    return 0; // 姓名不匹配,返回 0
}
 

以上便是duibi函数,它是通过一个库函数strcmp进行对比,如果相等的话就会返回一个零。

代码运行结果:

其实在黑名单的录入中,同样有着查重的功能,防止着编号重复,人员重复,与这里的核查功能有着异曲同工之妙,很简单,相信大家通过注释都能明白,那咱们换下一个

接下来是书籍编号查重,这是为了防止同一本书籍被不同的的人在相同的时间被借取,当然这个功能,博主设定为通过进行书籍编号和借阅时间的对比,来判断是否被借取,如果相同则无法借取本书。

if (duibi2(head, newNode->stunum, newNode->time)) 
{
    printf("该书籍编号已被%s在%s借阅,不能重复借阅!\n", newNode->name, newNode->time);
    free(newNode);
    system("pause");
    system("cls");
    return;
}

以上便是代码部分,它同样调用了自定义的duibi2函数,来完成了信息的判断,是否进入if语句。

代码运行结果:

最后便是排序功能的实现,

if (p == NULL)
{
    head = newNode;
    printf("head is null\n");


}
else if (p!= NULL && newNode->stunum < p->stunum)//新输入的数据比头节点小,且头节点不为空
{
    newNode->pNext = p;//指向比它大的节点
    head = newNode;//将头节点开辟的空间拿来放新节点,新结点的空间用来当临时空间

}

else
{
    while (p!= NULL && newNode->stunum > p->stunum)
    {
        prev = p;//记录新节点前面那个小于他的结点的地址
        p = p->pNext;//将地址换成下一个,继续比较
    }
    if (prev == NULL)//当 prev =null时,已经找遍了整条链表,所以新节点放在最后
    {
        // 如果 prev 为 NULL,说明新节点应该插入到链表当前的头节点
        newNode->pNext = head;
        head = newNode;
    }
    else//当找到了不比新节点小的记录,就把节点插在前面。
    {
        prev->pNext = newNode;//之前比新节点小的节点指向新节点
        newNode->pNext = p;//再将新节点指向头结点中的空。直到下一次录入
    }
}

相信大家看注释应该能看懂,所以咱就不详细讲了。

直接上代码运行结果:

我们可以看到这是最开始实验数据的排序,接下来我分别输入一个5和7,看看它们会不会顺利排序。

 

2.4.2删除与修改的区别

下图是修改的代码

void library_xiugai(void)//修改
{
	int num;
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要修改的书籍编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	xinxi* temp = head;
	while (temp != NULL)//头指针内不为空
	{
		if (temp->stunum == num)
		{
			break;//跳出循环(筛选)
		}
		temp = temp->pNext;//到下一个节点
	}
	if (temp == NULL)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("没有匹配学号:\n");
		set_text_color(COLOR_RESET); // 重置颜色
	}
	else
	{
		set_text_color(COLOR_CYAN);//赋予颜色
		printf("找到对应的信息如下:\n");
		printf("编号:%d,姓名:%s,性别:%c,借阅时间:%s,电话号码:%s\n", temp->stunum, temp->name, temp->sex, temp->time, temp->arr);
		printf("是否要修改成绩?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();//清空输入缓冲区,以正确的读取输入用户输入的单个字符
		char choose = getchar();
		if (choose == 'y' || choose == 'Y')
		{
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("请输入新的借阅时间:");
			set_text_color(COLOR_RESET); // 重置颜色
			scanf("%s", &temp->time);
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("请输入借阅人的新电话号码:");
			set_text_color(COLOR_RESET); // 重置颜色
			scanf("%s", &temp->arr);
		}


	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

printf("请输入新的借阅时间:");
scanf("%s", &temp->time);
printf("请输入借阅人的新电话号码:");
scanf("%s", &temp->arr);

修改是再找到对应的节点之后直接录入信息,本质是覆盖。就像你写一篇文章,写错了,拿上涂改液,修正贴,改完了,在本质上,错字依旧存在,只不过是被盖起来挡住了。

然后是删除

void library_shanchu(void)
{
	int num;
	renan* q = head1;//用来声明一个指向renan结构体的指针p,并将其初始化为头节点
	renan* prev1 = NULL;//用来声明一个指向renan结构体的指针prev1,用于记录遍历过程中的前一个地址
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要删除的黑名单人员编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	if (head1== NULL)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("链表为空,没有数据\n");
		system("pause");
		set_text_color(COLOR_RESET); // 重置颜色
		return;
	}
	if (head1->stunum == num)
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("编号:%d,姓名:%s\n,性别:%c,原因:%s,电话号码:%s \n",q->stunum, q->name,q->sex1, q->yuan,q->ard);
		printf("是否要删除记录?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();清除缓冲区分隔符,避免影响后面字符的读取
		char choose = getchar();//获取用户选择
		if (choose == 'y' || choose == 'Y')
		{
			set_text_color(COLOR_YELLOW);//赋予颜色
			prev1 = q->pNext;
			free(q);//释放源头节点的内存
			head1 = prev1;//将指针指向头节点的一节点
			printf("删除编号%d的黑名单人员成功!\n", num);
			set_text_color(COLOR_RESET); // 重置颜色

		}
		set_text_color(COLOR_CYAN);//赋予颜色
		system("pause");
		system("cls");
		set_text_color(COLOR_RESET); // 重置颜色
		return;
	}


	while (q != NULL && q->stunum != num)
	{
		prev1 = q;
		q= q->pNext;
	}
	if (q != NULL)
	{
		set_text_color(COLOR_MAGENTA);//赋予颜色
		printf("找到的信息如下:\n");
		printf("编号:%d,姓名:%s,性别:%c,原因:%s,电话号码:%s\n",q->stunum,q->name, q->sex1, q->yuan,q->ard);
		printf("是否要删除记录?(y/n)");
		set_text_color(COLOR_RESET); // 重置颜色
		getchar();清除缓冲区分隔符,避免影响后面字符的读取
		char choose = getchar();//获取用户选择
		if (choose == 'y' || choose == 'Y')
		{
			prev1->pNext = q->pNext;//更新前一个节点的指针,跳过要删除的节点
			free(q);//释放源头节点的内存
			set_text_color(COLOR_GREEN);//赋予颜色
			printf("删除编号%d的黑名单人员成功!\n", num);
			set_text_color(COLOR_RESET); // 重置颜色
		}
		else
		{
			set_text_color(COLOR_YELLOW);//赋予颜色
			printf("编号%d的黑名单人员未找到。\n", num);
			set_text_color(COLOR_RESET); // 重置颜色
		}
	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
	return;
}

prev1 = q->pNext;
free(q);//释放源头节点的内存
head1 = prev1;//将指针指向头节点的一节点
printf("删除编号%d的黑名单人员成功!\n", num);

        删除是直接释放掉要删除对象的内存空间,然后将前后连接在一起。本质上是真没了。就像你有一杯水,你将杯子砸了个细碎,并丢掉,你的水还有地方装吗?这就是删除, 而free()这个库函数便是帮助你砸掉杯子的工具,注意杯子是申请的内存空间,砸掉是取消掉,并不是真的把内存撕掉一部分丢了。

总结一下,修改是覆盖,删除是释放空间

2.4.3library_chaxun

我们在讲一下library_chaxun函数,先看代码

void library_chaxun(void)//查询
{
	int num;
	set_text_color(COLOR_CYAN);//赋予颜色
	printf("请输入要查找的书籍编号:");
	set_text_color(COLOR_RESET); // 重置颜色
	scanf("%d", &num);
	xinxi* temp = head;
	int found = 0;
	while (temp != NULL)//头指针内不为空
	{
		if (temp->stunum == num)
		{
			
			found++ ; // 找到匹配的书籍编号
			set_text_color(COLOR_MAGENTA);//赋予颜色
			printf("找到对应的信息如下:\n");
			printf("编号:%d,姓名:%s,性别:%c,借阅时间:%s,电话号码:%s\n", temp->stunum, temp->name, temp->sex, temp->time, temp->arr);
			set_text_color(COLOR_RESET); // 重置颜色
		}
		temp = temp->pNext;
	}
	if (!found)
	{
		set_text_color(COLOR_YELLOW);//赋予颜色
		printf("没有匹配学号:\n");
		set_text_color(COLOR_RESET); // 重置颜色

	}
	set_text_color(COLOR_CYAN);//赋予颜色
	system("pause");
	system("cls");
	set_text_color(COLOR_RESET); // 重置颜色
}

       首先 int found = 0;,它是干什么的呢?它是用来计数的,记录在遍历链表的过程中,找到了多少个和输入的编号相同的信息,这个变量在后面也有用到。

if (!found)
    {
        printf("没有匹配学号:\n");
    }
 

      当它一个也没找到的时候found就为零,因为有,所以取反,当found为0时,取反,就可以进入if语句,表示没有匹配学号,当found不为0时,同样取相反的一面,真取反为假,则无法进入if语句。正好表示成功查询到了信息。

while (temp != NULL)//头指针内不为空,为空之前出不来
    {
        if (temp->stunum == num)//找到链表中相等的部分则进入if语句一次
        {
            
            found++ ; // 找到匹配的书籍编号,找到一次加一
            printf("找到对应的信息如下:\n");
            printf("编号:%d,姓名:%s,性别:%c,借阅时间:%s,电话号码:%s\n", temp->stunum, temp->name, temp->sex, temp->time, temp->arr);
        }
        temp = temp->pNext;
    }

       看上面的代码,因为while语句和if语句的配合,所以遍历完整条链表之前,是不会退出循环的,所以,查询函数会打印出所有与输入编号相关的借阅记录。

        下面是代码运行效果:

          首先咱们先展示一下实验数据

        然后咱们在录入一个书籍相同编号,但借阅时间不同的借阅记录,记住相同的一本书,不可能在同一时间被借两次(至少博主开的这家图书馆每种书就一本),不然是无法录入的哦!

        然后再展示一下,是否成功录入

         很好,录入成功,那我们最后来查询一下试试

         然后咱们在展示一下.h文件,gongleng.h

#pragma once//编译环境自带
#ifndef GONGLENG_H//固定格式
#define GONGLENG_H//固定格式

ypedef struct xinxi//定义结构体,用于存放学生信息
{
	int stunum;//存放书籍编号
	char name[20];//存放借阅人员姓名
	char sex;//存放性别
	char time[30];//借阅时间
	char arr[20];//存放电话号码
	struct xinxi* pNext;//pNxet,是指针类型,指向下一个Student结构体,用于构建链表
}xinxi;

typedef struct heimingdanrenyuan//定义结构体,用于存放学生信息
{
	int stunum;//人员编号
	char name[20];//黑名单人员姓名
	char sex1;//性别
	char yuan[20];//进入黑名单的原因
	char ard[15];//存放电话号码
	struct heimingdanrenyuan* pNext;//pNxet,是指针类型,指向下一个Student结构体,用于构建链表
}renan;

int duibi(renan* head, xinxi* head1);//对比
int duibi2(xinxi* head, int stunum, char* time);
int duibi3(renan* head1, int stunum);
int duibi4(renan* head1, char* name, char* ard);
//正常图书馆功能
void library_shuju();//实验数据
void library_luru();//录入
void library_chaxun();//查询
void library_xiugai();//修改
void library_zhanshi();//展示
//黑名单功能
void library_shuju2();//实验数据
void library_luru2();//录入
void library_chaxun2();//查询
void library_xiugai2();//修改黑名单人员信息
void library_zhanshi2();//展示
void library_shanchu(void);//删除
#endif

        以上便是存放的函数声明,还有两条链表的结构体声明。 

2.5heimingdan文件

        下图是heimingdan.c文件

#define _CRT_SECURE_NO_WARNINGS 1//编译环境自带
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include"heimingdan.h"
#include"menu.h"
#include"color.h"
void library_heimingdan(void)
{
	library_shuju2();//实验数据
	int choose = -1;
	while (choose)
	{
		libreary_menu2();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("查询\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_chaxun2();
			break;
		case 2:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("录入\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_luru2();
			break;
		case 3:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("修改\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_xiugai2();
			break;
		case 4:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("展示\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_zhanshi2(); //展示
			break;
		case 5:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("删除\n");
			set_text_color(COLOR_RESET); // 重置颜色
			library_shanchu();//删除
			break;
		case 6:
			set_text_color(COLOR_CYAN);//赋予颜色
			printf("退出\n");
			system("pause");
			system("cls");
			set_text_color(COLOR_RESET); // 重置颜色
			return 0;
		default:
			set_text_color(COLOR_YELLOW);//赋予颜色
			printf("没有其他选项哦!\n");
			system("pause");
			system("cls");
			set_text_color(COLOR_RESET); // 重置颜色
			break;

		}
	}


	return 0;
}

        这便是二级菜单,黑名单。很简单吧?创造二级菜单看起来很难,但其实只是调用了黑名单的函数 罢了。

        下面是.h文件展示heimingdan.h

#pragma once
#ifndef HEIMINGDAN_H
#define HEIMINGDAN_H
void library_heimingdan();//黑名单



#endif
2.6color文件 

        color是颜色模块的封装函数了,大家一定很好奇为什么说是有颜色模块,但图片里展示的却只有黑白色呢?那是因为它们只是没有启用颜色模块的情况下的样子,这样对比起来才明显不是吗?,咱们先看代码,color.c文件

#define _CRT_SECURE_NO_WARNINGS 1//编译环境自带
#include<stdio.h>
#include"color.h"

// 新函数:设置文本颜色
void set_text_color(const char* color)
{
	printf("%s", color);//打印出颜色
}

        然后咱们再看看,color.h文件

#pragma once//编译环境自带
#ifndef COLOR_H
#define COLOR_H

// 定义颜色常量
#define COLOR_RESET   "\033[0m"//返回默认值
#define COLOR_RED     "\033[31m"//红色
#define COLOR_GREEN   "\033[32m"//绿色
#define COLOR_YELLOW  "\033[33m"//黄色
#define COLOR_BLUE    "\033[34m"//深蓝色
#define COLOR_MAGENTA "\033[35m"//紫色
#define COLOR_CYAN    "\033[36m"//淡蓝色
#define COLOR_WHITE   "\033[37m"//白色

void set_text_color(const char* color);//赋予颜色
#endif

       光从代码上来看,是不是很简单,只有那短短的几行代码,实际上它也很简单,它的函数组成很简单,就是接受一个参数,然后打印出来,真正重要的是它的参数,通过.h中的宏定义,提前设定好了参数,每一种颜色名称,就对应了一个ANSI 转义序列,而转义序列,又分为两部分,就比如\033[0m,它就是一个ANSI 转义序列,其中\033是转义字符,而[0m则是控制码,在这个控制码中,0代表恢复表示重置所有属性到默认值,m 表示这是一个控制字符序列的结束标志。那么问题来了,ANSI 转义序列,只能用来设置颜色吗?不是的,ANSI 转义序列不仅可以用来设置颜色,还可以用来设置其他文本属性,如加粗、下划线、闪烁等

        以下是一些常见的 ANSI 转义序列及其功能:

  • \033[0m:重置所有属性到默认值。
  • \033[1m:设置文本为加粗。
  • \033[4m:设置文本为下划线。
  • \033[5m:设置文本为闪烁。
  • \033[7m:设置文本为反显(前景色和背景色互换)。
  • \033[31m:设置文本颜色为红色。
  • \033[32m:设置文本颜色为绿色。
  • \033[33m:设置文本颜色为黄色。
  • \033[34m:设置文本颜色为蓝色。
  • \033[35m:设置文本颜色为紫色。
  • \033[36m:设置文本颜色为青色。
  • \033[37m:设置文本颜色为白色

并且它们还可以组合使用达成更复杂的文本样式。

好了就不多说了

接下来,我们来看看成品

 

三、结束 

       这便是整个图书馆信息管理系统的所有讲解了,以上代码,博主是通过vs2022编译环境完成的,如果其他编译器运行有问题的话 ,可能是库函数设定不同需要自行调整,又或者需要将编译环境自带的代码给删掉哦!

      希望可以给大家C语言编程学习的的路途中带来那么一点点的思路。


完 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值