c语言实现图书管理系统,未成熟版

c语言实现图书管理系统,未成熟版

一、目的

创建一个图书管理系统,进行添加、删除,查询,借书,还书,退出,等等操作;使用菜单以实现功能选择模块。

二、C语言图书管理系统包括以下功能:

0. 退出信息系统

1. 登记书籍信息

2. 浏览书籍查询

3. 借阅书籍

4. 归还书籍

5.删除书籍

6.查找书籍

三、流程图

四、代码块

/*定义#define _CRT_SECURE_NO_WARNINGS,
我们在编译老的用C语言的开源项目如lua源包的时候,
可能因为一些老的.c文件使用了strcpy,scanf等不安全的函数,
而报警告和错误,而导致无法编译通过。*/
#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <stdlib.h> //数据的设计
#include <string.h>



//3.数据的设计
	//3.1程序用什么东西处理数据(数组或链表)--->选择链表
	//3.2数据的结构(图书的信息)

//图书信息
struct bookInfo
{
	char name[20]; //名字
	float price; //价格
	int num; //数量
};

struct Node //Node节点
{
	struct bookInfo data; //链表里存的struct bookInfo类型
	struct Node* next;
};

struct Node* list = NULL; //更改为全局的列表为空,然后在主函数那初始化

//创建表头:表头就是一个结构体变量
struct Node* createHead()
{
	/*动态内存申请
	sizeof(struct node)就是求 struct node 这个结构体占用的字节数。
	malloc(sizeof(struct node))申请 struct node 这个结构体占用字节数大小的空间
	(struct node *) malloc(sizeof(struct node))将申请的空间的地址强制转化为 struct node * 指针类型
	headNade=(struct node *) malloc(sizeof(struct node))将那个强制转化的地址赋值给 headNade.
	 */
	struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));

	//变量的基本规则:使用前必须初始化,在这里只需要给next指针初始化,表头不需要存储数据,所以不用初始化
	headNode->next = NULL; //表头不存数据
	return headNode; //返回创建链表的头指针
};


//创建节点,为插入做准备
//用户的数据变为结构体的变量
struct Node* createNode(struct bookInfo data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
};



//插入,表头法插入
void insertNodeByHead(struct Node* headNode,struct bookInfo data)
{
    struct Node* newNode = createNode(data);    //通过调用子函数,创建结点
	//必须先连接,后断开
    newNode->next = headNode->next;      //头插法,headnode->next为下一个结点(表头下一个结点)的地址,newnode的指针域指向下一个结点(地址)
    headNode->next = newNode;     // 原表头指向新建的结点(地址)
}

//指定位置删除
//posLeftNode->next=posNode->next;相邻指针
//free(posNode);指定删除的代码,删除完后要把posNode=NULL
void deleteNodeByName(struct Node* headNode,char *bookName) //通过书籍名字进行删除
{
	/*需要两个指针进行删除,先删除节点指针的Left左边,其次再删除节点指针的右边,一前一后*/
	struct Node* posLeftNode = headNode;
	struct Node* posNode = headNode->next;
	/*书籍名字是字符串,所以要使用字符串比较函数处理
	当我们删除节点不等于空,
	并且使用strcmp()比较,
	使用posNode->data.name和我们传过来的参数bookName进行比较*/
	while(posNode != NULL && strcmp(posNode->data.name,bookName)) 
	{
	/*让节点并排往下走*/
		posLeftNode = posNode;
		posNode = posLeftNode->next;
	}

	//讨论查找结果,如果posNode为空,那就是没有找到,无法删除,找到就删除
	if(posNode == NULL)
		return;
	else
	{
		printf("删除成功!\n");
		posLeftNode->next = posNode->next;
		free(posNode);
		posNode = NULL;
	}
}

//添加链表的查找函数,把找到的节点给它返回
struct Node* searchByName(struct Node* headNode,char *bookName)
{
	//只需要找到那个节点即可,不需要两个指针
	struct Node* posNode = headNode->next;
	while(posNode != NULL && strcmp(posNode->data.name,bookName)) //strcmp()比较括号里面的,如果相等,就退出循环,如果不等,那就执行循环
	{
		posNode = posNode->next;
	}
	return posNode;
}


//打印链表
void printList(struct Node* headNode)
{
	/*定义一个移动的指针pMove从第二个节点headNode->next开始打印*/
	struct Node* pMove = headNode->next;
	printf("书名\t价格\t数量\n"); //表头
	while(pMove != NULL) // pMove不等于空的时候,打印节点的数据
	{
		//剥洋葱,一层一层
		printf("%s\t%0.1f\t%d\n", pMove->data.name,  pMove->data.price,  pMove->data.num); //让pMove往下一个节点走
		pMove =  pMove->next;
	}
}


//1.写界面--->菜单--->模块
void makeMenu()
{
	printf("\t                                                                                                                                \n");
	printf("\t┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
	printf("\t┃ ╔══════════════════════════════════════════════════════════╗ ┃\n");
	printf("\t┃ ║            欢 迎 使 用 图 书 管 理 信 息 系 统           ║ ┃\n");
	printf("\t┃ ╚══════════════════════════════════════════════════════════╝ ┃\n");
	printf("\t┃ ***********************************************************  ┃\n"); 
	printf("\t┃ *                          *                              *  ┃\n"); 
	printf("\t┃ *         0.退出信息系统   *      1.登记书籍信息          *  ┃\n"); 
	printf("\t┃ *                          *                              *  ┃\n"); 
	printf("\t┃ ***********************************************************  ┃\n"); 
	printf("\t┃ *                          *                              *  ┃\n"); 
	printf("\t┃ *         2.浏览书籍查询   *      3.借阅书籍              *  ┃\n"); 
	printf("\t┃ *                          *                              *  ┃\n"); 
	printf("\t┃ ***********************************************************  ┃\n"); 
	printf("\t┃ *                          *                              *  ┃\n");
	printf("\t┃ *         4.归还书籍       *      5.删除书籍              *  ┃\n");
	printf("\t┃ *                          *                              *  ┃\n");
	printf("\t┃ ***********************************************************  ┃\n"); 
	printf("\t┃ *                          *                              *  ┃\n");
	printf("\t┃ *         6.查找书籍       *                              *  ┃\n");
	printf("\t┃ *                          *                              *  ┃\n");
	printf("\t┃ *                          *                              *  ┃\n");
	printf("\t┃ ***********************************************************  ┃\n"); 
	printf("\t┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"); 
	printf("请输入0-8:");
}

//文件存操作
void saveInfoToFile(const char *fileName,struct Node* headNode) //将链表里的信息打印到文件里去
{
	FILE *fp = fopen(fileName, "w"); //第一个指针使用写的方式打开文件
	struct Node* pMove = headNode->next;
	//当第二个节点不等于空时,就将链表里的信息打印到文件里去
	while(pMove) 
	{
		fprintf(fp,"%s\t%.1f\t%d\n", pMove->data.name,  pMove->data.price,  pMove->data.num); //打印到指针文件指向的文件,打印的是pMove
		pMove = pMove->next;
	}

	fclose(fp); //关闭文件
}

//文件读操作
void readInfoFromFile(const char* fileName, struct Node* headNode)  //需要从文件里将内容读出
{
	//把文件当作是一个输入键盘,定义临时变量tempData,把数据读到变量(如:%s)里
	struct bookInfo tempData;

	FILE* fp = fopen(fileName, "r"); //第一个指针使用读的方式打开文件

	/*文件开始的时候是不存在的,不存在的时候,使用创建(w+)的读写方式打开文件*/
	if (fp == NULL)
	{
		fp = fopen(fileName, "w+");
	}

	while (fscanf(fp,"%s\t%.1f\t%d\n", tempData.name, &tempData.price, &tempData.num) != EOF) //加上" != EOF"后该程序就不是死循环了
	{

		/*做链表的插入,读一个信息就插入到链表中*/
		insertNodeByHead(list, tempData);
	}

	fclose(fp); //关闭文件
}



//按键处理,做交互
void keyDown()
{
	int userKey = 0;
	//准备一个临时变量去存放书籍的临时信息
	struct bookInfo tempBook;
	//写个临时的指针用于查询
	struct Node* result = NULL;
	scanf("%d",&userKey);
	switch (userKey)
	{
	case 0:
		printf("【退出】\n");
		printf("退出成功!欢迎下次使用~\n");
		system("pause"); //防止闪屏
		exit(0); //关闭掉整个程序
		break;
	case 1:
		printf("【登记】\n");
		printf("请输入书籍的信息(name,price,num):");
		scanf("%s%f%d",tempBook.name,&tempBook.price,&tempBook.num); //tempBook.name不需要加&取地址,因为是字符串
		insertNodeByHead(list, tempBook); // 插入链表
		saveInfoToFile("bookinfo.txt", list); //当做了数据修改,就将信息保存到bookinfo.txt文件中,读到链表中
		break;
	case 2:
		printf("【浏览】\n");
		printList(list); //打印链表
		break;
	case 3:
		printf("【借阅】\n"); //是把书的数量减少,书籍存在可以借,书的数量-1,不存在则提示借书失败
		//通过书籍名去借阅
		printf("请输入需要借阅的书名:");
		scanf("%s", tempBook.name);
		result = searchByName(list, tempBook.name);
			if(result == NULL)
		{
			printf("没有此书籍,无法借阅!");
		}
		else
		{
			if(result->data.num > 0) //大于0说明有书籍可借阅
			{
				result->data.num--; //书的数量-1
				printf("【0v0】恭喜你,借阅成功!");
			}
			else
			{
				printf("【TAT】当前书籍无库存,借阅失败");
			}
		}
		break;
	case 4:
		printf("【归还】\n"); //是把当前书的数量增加+1
			printf("请输入需要归还的书名:");
		scanf("%s", tempBook.name);
		result = searchByName(list, tempBook.name);
			if(result == NULL)
		{
			printf("没有此书籍借阅记录,无法归还!");
		}
		else
		{
				result->data.num++; //书的数量+1
				printf("【0v0】恭喜你,归还成功!");
		}
		break;
	case 5:
		printf("【删除】\n");
		printf("请输入要删除书籍的名字:");
		scanf("%s",tempBook.name);
		deleteNodeByName(list, tempBook.name);
		saveInfoToFile("bookinfo.txt", list); //删除之后需要同步到文件,因为涉及到数据的修改
		break;
	case 6:
		printf("【查找】\n");
		printf("请输入要查询书籍的名字:");
		scanf("%s",tempBook.name);
		result = searchByName(list, tempBook.name);
		//查找等效于链表的查找
		//判断如果result等于NULL,就表示未找到
		if(result == NULL)
		{
			printf("未找到该书籍!");
		}
		else
		{
			printf("书名\t价格\t数量\n");
			printf("%s\t%.1f\t%d\n",result->data.name,result->data.price,result->data.num);
		}
		break;
	default:
		printf("【错误】\n"); //在switch语句中,如果表达式的值与每一中情况都不同,则执行default:后面的语句
		break;

	}
}



//调用测试实现
int main()
{
	list = createHead();
	readInfoFromFile("bookinfo.txt", list); //当系统运行的时候,就将文件的东西读到列表里

	/*while(1)其中1代表一个常量表达式,
	它永远不会等于0。循环会一直执行下去。
	除非你设置break等类似的跳出循环语句循环才会中止。*/
	while (1)
	{
		makeMenu();
		keyDown();
		system("pause");
		system("cls"); //清屏代码:
	}

	system("pause"); //就是暂停程序的执行,等待任意健继续执行。
	return 0;
}

编辑于 2021-12-03 12:59