C项目--带权限的图书管理系统(1000多行代码,代码数据可下载,极其适合初学练手)

news2024/11/6 7:30:32

本专栏目的

  • 更新C/C++的相关的项目

前言

  • C语言的图书权限管理系统完结(进阶的一点后面更新),1000多行代码(核心代码5、600行);
  • 本设计是一个比较综合的练习,用到数据结构(顺序表、链表、静态链表)、文件、排序、查找、字符串操作等方面的知识;
  • 由于本项目是本人一步一步边做边写笔记,难免会有失误,有失误看最后下载网盘代码即可
  • 制作很不容易,望大家点赞 + 收藏 + 关注。

简要说明

本设计是一个比较综合的练习,用到顺序表、链表、静态链表、文件、排序、查找、字符串操作等方面的知识。

  • 大体架构

在这里插入图片描述

  • 简要关系

在这里插入图片描述

  • 编译器:vs2022

前置知识

  • 之前更新的知识点

  • void*,如下:

  • int a = 10;
    void* p = a;    // 说明p存储的是**  字节序列  **,告诉编译器不关注int类型,只是存储字节序列
    printf("%d\n", (int)p);   // 告诉编译器说,p存储的字节序列转化成int类型输出
    
  • 从上可以说明,void* 在C语言中是万能数据类型,可以存储任意数据

文章目录

  • Part1
    • 0、创建入口函数main
    • 1、菜单
    • 2、数据存储--顺序表
    • 3、数据存储--链表
    • 4、用户管理需求
    • 5、用户管理框架搭建
      • 用户信息
      • 用户管理
      • 检验框架
    • 6、用户功能的实现
      • 从文件加载用户信息成功
      • 用户信息显示实现
      • 用户信息修改
      • 用户信息输入
      • 用户密码修改
      • 用户信息删除
      • 小结
    • 7、框架的整理
  • part2
    • 0、读者管理需求
    • 1、读者管理框架搭建
      • 读者信息
      • 读者管理
    • 2、读者功能的实现
      • 从文件加载数据
      • 读者信息显示
      • 读者信息输入
      • 读者信息查询
      • 读者信息删除
      • 读者信息修改
      • 小结
  • Part3
    • 0、图书管理需求
    • 1、图书模块搭建
      • 图书信息
      • 框架的搭建
    • 2、图书功能实现
      • 图书信息输入实现
      • 图书信息修改实现
      • 图书查询功能实现
        • 按书号查询
        • 按书名查询
        • 按作者名查询
        • 按出版社查询
      • 汇总功能实现
      • 小结
  • Part4
    • 0、图书流通子系统需求
    • 1、图书流通信息
    • 2、框架搭建
    • 3、功能实现
    • 4、测试
  • Part5
    • 0、权限需求
    • 1、权限分析与准备
    • 2、功能实现
      • 图书流通
      • 图书管理
      • 读者模块
      • 用户模块
    • 3、保存完善
    • 4、进阶
  • 代码网盘

Part1

0、创建入口函数main

创建**“main.c”**

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{

	
	return 0;
}

1、菜单

创建**"Menu.h"和“Menu.c”**文件,目录如下:

在这里插入图片描述

在"Menu.h"文件中,创建不同菜单的API接口

#ifndef MENU_H_
#define MENU_H_

/*@Autor: sheep*/
/*@菜单*/

// 首页欢迎菜单
int	welcomeMenu();
// 主菜单
int mainMenu();
// 用户管理
int userMenu();
// 读者管理
int readerMenu();
// 图书管理
int managerMenu();
// 图书流通
int circulate();

// 图书查询:图书管理中的功能
int searchMenu();

#endif // !MENU_H_

API实现在"Menu.c"文件中

#define _CRT_SECURE_NO_WARNINGS

#include "Menu.h"
#include <stdlib.h>
#include <stdio.h>

// 选择
static int select()
{
	// 定义要输入的选择变量,赋值为-1的原因:返回未初始化的变量很危险
	int n = -1;
	// 清屏
	printf("请输入您的选项>>: ");
	int res = scanf("%d", &n);
	return n;
}

int	welcomeMenu()
{
	system("cls");
	printf("******欢迎使用图书信息馆管理系统****\n");
	printf("*	    1.用户登录	           *\n");
	printf("*	    2.退出系统	           *\n");
	printf("************************************\n");
	return select();
}

int mainMenu()
{
	system("cls");
	printf("************************************\n");
	printf("*            1.用户管理            *\n");
	printf("*            2.读者管理            *\n");
	printf("*            3.图书管理            *\n");
	printf("*            4.图书流通管理        *\n");
	printf("*            5.退出管理            *\n");
	printf("************************************\n");
	return select();
}

int userMenu()
{
	system("cls");
	printf("************************************\n");
	printf("*          1.用户信息输入          *\n");
	printf("*          2.用户信息修改          *\n");
	printf("*          3.用户信息删除          *\n");
	printf("*          4.用户信息显示          *\n");
	printf("*          5.用户密码修改          *\n");
	printf("*          0.返回主菜单            *\n");
	printf("************************************\n");
	return select();
}

int readerMenu()
{
	system("cls");
	printf("************************************\n");
	printf("*          1.读者信息输入          *\n");
	printf("*          2.读者信息修改          *\n");
	printf("*          3.读者信息删除          *\n");
	printf("*          4.读者信息显示          *\n");
	printf("*          5.读者密码修改          *\n");
	printf("*          0.返回主菜单            *\n");
	printf("************************************\n");
	return  select();
}

int managerMenu()
{
	system("cls");
	printf("************************************\n");
	printf("*          1.图书信息输入          *\n");
	printf("*          2.图书信息修改          *\n");
	printf("*          3.图书信息查询          *\n");
	printf("*          4.统计汇总              *\n");
	printf("*          0.返回主菜单            *\n");
	printf("************************************\n");
	return select();
}
int searchMenu()
{
	system("cls");
	printf("************************************\n");
	printf("*          1.按书号查询            *\n");
	printf("*          2.按书名查询            *\n");
	printf("*          3.按作者查询            *\n");
	printf("*          4.按出版社查询          *\n");
	printf("*          0.返回主菜单            *\n");
	printf("************************************\n");

	return select();
}

int circulate()
{
	system("cls");
	printf("************************************\n");
	printf("*          1.借书处理              *\n");
	printf("*          2.还书处理              *\n");
	printf("*          0.返回主菜单            *\n");
	printf("************************************\n");
	return select();
}

效果

在main函数中分别调用这些API接口,如下:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include "Menu.h"

int main()
{
	welcomeMenu();

	mainMenu();

	userMenu();

	readerMenu();

	managerMenu();

	circulate();

	searchMenu();

	return 0;
}

效果如下:

在这里插入图片描述

因为在上面实现菜单的时候,在每一个输出前都调用了**"system(“cls)”**这个API,为了展示,我们可以先注释掉这些,这个时候输出就可以看到全部菜单显示了:

在这里插入图片描述

2、数据存储–顺序表

实现功能:增删

创建**”SeqList.h“和”SeqList.c"**,目录如下:

在这里插入图片描述

"SeqList.h"文件中创建‘增删’API

#ifndef SEQLIST_H_
#define SEQLIST_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SEQLIST_INIT_SIZE 10

typedef struct SeqList {
	int size;				//当前元素
	int capacity;			//容量大小
	void** data;			//数据
}SeqList;

// 初始化顺序表
void SeqList_init(SeqList* plist);

// 插入数据
void SeqList_pushBack(SeqList* plist, void* value);

// 清空顺序表
void SeqList_clear(SeqList* plist);

// 销毁顺序表
void SeqList_destory(SeqList* plist);

#endif // !SEQLIST_H_

"SeqList.c"文件中实现

#include "SeqList.h"

// 初始化顺序表
void SeqList_init(SeqList* plist)
{
	plist->size = 0;
	plist->capacity = SEQLIST_INIT_SIZE;
	plist->data = malloc((plist->capacity) * sizeof(void*));
	memset(plist->data, 0, sizeof(void*) * plist->capacity);
}

// 插入数据
void SeqList_pushBack(SeqList* plist, void* value)
{
	if (plist->size == plist->capacity) {
		// 扩容
		plist->data = realloc(plist->data, (plist->capacity + 10) * sizeof(void*));
		if (!plist->data) {
			printf("%s 扩容申请内存失败\n", __FUNCTION__);
			return;
		}
		// 数据变化
		plist->capacity += 10;
		plist->data[plist->size++] = value;
	}
	else {
		plist->data[plist->size++] = value;
	}
}

// 清空顺序表
void SeqList_clear(SeqList* plist)
{
	plist->size = 0;
}

void SeqList_destory(SeqList* plist)
{
	// 判断是否是空指针
	if (!plist) return;
	// 清空数据
	plist->capacity = 0;
	plist->size = 0;
	free(plist->data);
	plist->data = NULL;
}

插入数据验证

在这里插入图片描述

3、数据存储–链表

暂时先实现增删

创建"List.h"和”List.c“文件,结构如图:

在这里插入图片描述

在”List.h"文件中定义API接口,如下:

#ifndef LIST_H_
#define LIST_H_

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 有头链表
/*@节点*/
typedef struct Node {
	void* data;				// 数据
	struct Node* next;		// 下一个节点
}Node;

/*@链表*/
typedef struct List {
	int size;
	Node* front;		// 头节点
	Node* tail;			// 尾节点
}List;


// 初始化链表
void list_init(List* plist);

// 插入元素
void list_pushBack(List* plist, void* data);

// 删除元素
void list_removeOne(List* plist, void* value);

// 销毁链表
void list_destory(List* plist);

#endif // !LIST_H_

在“List.c”文件中实现API接口

#include "List.h"

#include <stdbool.h>
#include <stdlib.h>

// 本文件用
static bool isVailed(List* plist)
{
	if (NULL == plist) {
		printf("function: %s  List* is NULL\n", __FUNCTION__);
		return false;
	}
	return true;
}

Node* createNode(void* data)
{
	if (NULL == data) {
		printf("function is: %s, data is NULL\n", __FUNCTION__);
		return NULL;
	}

	// 创建节点
	Node* new_node = malloc(sizeof(Node));
	if (!new_node) {
		printf("function is %s, malloc failed\n", __FUNCTION__);
		return;
	}
	new_node->next = NULL;    // 防止为空指针
	// 赋值数据
	new_node->data = data;

	return new_node;
}

void list_init(List* plist)
{
	// 检查是否为空指针
	if (!isVailed(plist)) {
		return;
	}

	// calloc会将里面参数赋值为 0
	plist->front = plist->tail = calloc(1, sizeof(Node*));
	if (NULL == plist->front) {
		printf("function: %s  申请内存失败\n", __FUNCTION__);
		return;
	}
	// 赋值
	plist->size = 0;
}

void list_pushBack(List* plist, void* data)
{
	if (!isVailed(plist)) {
		return;
	}

	// 尾插法
	Node* new_node = createNode(data);
	plist->tail->next = new_node;		// 插入
	plist->tail = new_node;				// 尾节点移位
	plist->size++;
}

void list_removeOne(List* plist, void* value)
{
	if (!isVailed(plist)) {
		printf("function is %s, list is null\n", __FUNCTION__);
		return;
	}

	// 获取指针
	Node* cur = plist->front->next;
	Node* pre = plist->front;

	while (cur) {
		if (cur->data == value) {
            pre->next = cur->next;
			break;
		}

		// 更新节点
		pre = cur;
		cur = cur->next;
	}

	// 删除
	if (NULL != cur) {
		pre->next = cur->next;
		free(cur);
	}
}

void list_destory(List* plist)
{
	if (!isVailed(plist)) {
		printf("function is: %s, list is null\n", __FUNCTION__);
		return;
	}

	Node* cur = plist->front->next;
	Node* pre = plist->front;

	// 删除每一个节点
	while (cur) {
		free(pre);
		pre = cur;
		cur = cur->next;
	}
	// 销毁最后一个
	free(cur);
}

插入数据验证

在这里插入图片描述

4、用户管理需求

应包括用户信息输入、用户信息修改、用户信息删除、用户信息显示、用户密码修改等功能。其中”系统管理员“可以使用上述全部功能,”图书管理员“和”普通读者“只能使用”用户密码修改功能“。

用户管理菜单要求包括如下选项

**********************************
		1.用户信息输入
		2.用户信息修改
		3.用户信息删除
		4.用户信息显示
		5.用户密码修改
		0.返回主菜单
**********************************

"用户管理子系统"涉及到的”用户信息文件“要求如下表所示,其中用户名时学号或教工号,用户密码要求由八位字母和数字组成。系统开始运行时要将该文件打开,将其内容读进来,形成一个单链表,在系统运行结束时再将单链表中的内容写回相应文件。

  • 用户信息文件
用户ID用户密码用户类型
19980171234567q1(系统管理员)
2001021S12345672(图书管理员)
1988003123W56783(普通读者)
20172568ZZ123qq93(普通读者)

5、用户管理框架搭建

用户信息

**创建“User.h"和”User.c"**文件,目录如下:

在这里插入图片描述

用户信息储存与处理相关API定义”User.h“

#ifndef USER_H_
#define USER_H_

// 用户类型编号
enum UserType {
	SYS_MANAGER = 1,
	BOOK_MANAGER,
	READER
};

typedef struct User {
	unsigned long long user_id;			// 用户ID
	char user_password[10];				// 用户密码
	int type;							// 用户类型
}User;

//创建一个未初始化用户
User* createUser();
//打印一个用户信息
void userMessPrintf(User* user);

#endif // !USER_H_

用户信息储存API实现"User.c"

#include "User.h"
#include <stdio.h>
#include <malloc.h>

User* createUser()
{
	User* new_user = calloc(1, sizeof(User));
	if (!new_user) {
		printf("function is: %s, calloc failed\n", __FUNCTION__);
		return new_user;
	}
	return new_user;
}

void userMessPrintf(User* user)
{
	printf("%-10llu %-10s %d\n", user->user_id, user->user_password, user->type);
}

用户管理

更具要求,用链表来储存用户信息、用户管理菜单一共有6个功能实现,这里先搭好框架。

创建”UserManager.h"和“UserManager.c”文件

在这里插入图片描述

UserManager.h的实现

#ifndef USERMANAGER_H_
#define USERMANAGER_H_

#include "List.h"

// 创建一个用户管理结构体
typedef struct UserManage {
	List listManager;				// 用户用链表存储
}UserManage;

// 用户信息初始化,如:创建链表,从文件中读取用户信息
void userManage_init(UserManage* userm, const char* filename);
// 用户管理处理工作
void userManage_operator(UserManage* userm);

// 用户信息输入
void userManage_input(UserManage* userm);
// 用户信息修改
void userManage_modify(UserManage* userm);
// 用户信息删除
void userManage_delete(UserManage* userm);
// 用户信息显示
void userManage_show(UserManage* userm);
// 用户密码修改
void userManage_modifyPassword(UserManage* userm);


#endif // !USERMANAGER_H_

UserManager.c的实现

#include "UserManager.h"
#include "List.h"
#include "Menu.h"

#include <Windows.h>

void userManage_init(UserManage* userm, const char* filename)
{
	// 初始化链表
	list_init(&userm->listManager);
}

void userManage_operator(UserManage* userm)
{
	bool isQuit = false;
	while (!isQuit) {
		int op = userMenu();
		switch (op)
		{
		case 1:		// 用户信息输入
			userManage_input(userm);
			break;
		case 2:		// 用户信息修改
			userManage_modify(userm);
			break;
		case 3:		// 用户信息删除
			userManage_delete(userm);
			break;
		case 4:		// 用户信息显示
			userManage_show(userm);
			break;
		case 5:		// 用户密码修改
			userManage_modifyPassword(userm);
			break;
		case 0:
			printf("欢迎下次使用\n");
			Sleep(1000);			//暂停1s
			isQuit = true;
			break;
		}
	}
}

void userManage_input(UserManage* userm)
{

}

void userManage_modify(UserManage* userm)
{

}

void userManage_delete(UserManage* userm)
{

}

void userManage_show(UserManage* userm)
{

}

void userManage_modifyPassword(UserManage* userm)
{

}

检验框架

在**“main.c”中测试**

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include "UserManager.h"

int main()
{
	UserManage manage;
	userManage_init(&manage, NULL);
	// 开始管理
	userManage_operator(&manage);

	return 0;
}

结果

在这里插入图片描述

注意:这里菜单依然注释了**“system(“cls”)”**这条语句

6、用户功能的实现

从文件加载用户信息成功

首先准备测试数据,新建数据文件:

在这里插入图片描述

user.txt中写入部分数据,中间用Tab键隔开

账号	密码	用户类型
1998017	1234567q 	1
2001021	S1234567	2
1988003	123W5678	3
20172568	ZZ123qq9	3

根据要求,要加载文件中数据到用户【链表】,故在“userManage.h”中添加新的API

// 用户信息加载(从文件中加载)
void userManage_load_from_file(UserManage* userm, const char* filename);

在**”userManage.c“**文件中实现:

// 在初始化中调用
void userManage_init(UserManage* userm, const char* filename)
{
	// 初始化链表
	list_init(&userm->listManager);
	// 加载数据
	userManage_load_from_file(userm, filename);
}

void userManage_load_from_file(UserManage* userm, const char* filename)
{
	// 打开文件
	FILE* fp = fopen(filename, "r");
	if (!fp) {
		perror("打开文件失败\n");
		return;
	}
	// 读取文件数据
	char buff[BUFSIZ] = { 0 };  	// 定义缓冲区
	// 读取标题数据: buff	0x00000044680ff470 "账号\t密码\t用户类型\n"	char[512]
	fgets(buff, BUFSIZ, fp);
	// 字符串形式读取数据
	while (!feof(fp))   //读取到文件末尾
	{
		// 一行一行读取
		fgets(buff, BUFSIZ, fp);
		// 分割
		User* user = createUser();
		int res = sscanf(buff, "%llu %s %d", &user.user_id, user.user_password, &user.type);
		if (res < 0) {
			printf("function is %s, file read failed\n", __FUNCTION__);
			return;
		}
		// 插入链表中
		list_pushBack(&userm->listManager, user);
	}
	// 读完关闭文件
	fclose(fp);
}

用户信息显示实现

由于后面经常需要用链表中的数据进行不同处理,故在链表中进行添加回调函数API,在**”List.h“**中添加:

// 处理每个数据的回调函数
typedef void (*LISTCALLBACK)(void*);
void list_transform(List* plist, LISTCALLBACK callback);

在”List.c“文件中进行实现

void list_transform(List* plist, LISTCALLBACK callback)
{
	if (!isVailed(plist)) {
		printf("function is %s, list is null\n", __FUNCTION__);
		return;
	}

	Node* cur = plist->front->next;
	// 将每一个数据通过回调函数进行处理
	while (cur) {
		callback(cur->data);
		cur = cur->next;
	}
}

测试,实现用户查询功能,看是否真的在文件中读取到了用户信息

// userManage.c
void userManage_show(UserManage* userm)
{
	// 输出标题
	printf("%-10s %-10s %-10s\n", "用户ID", "密码", "用户类型");
	// 运用回调函数输出
	list_transform(&userm->listManager, userMessPrintf);  // userMessPrintf在User.h/c文件中实现了
}

效果展示

  • main函数传递文件代码:

  • #define _CRT_SECURE_NO_WARNINGS
    
    #include <stdio.h>
    #include "UserManager.h"
    
    int main()
    {
    	UserManage manage;
    	userManage_init(&manage, "./data/user.txt");   // 传入文件代码
    	// 开始管理
    	userManage_operator(&manage);
    
    	return 0;
    }
    
  • 效果展示

  • 在这里插入图片描述

  • 用户信息加载成功

用户信息修改

用户信息有三个字段,用户ID、密码、类型,其中只能修改类型,ID是不可能修改的,密码能修改但是后面5是专门修改密码的,故只能修改类型

修改就要去查找,通过用户ID查找无疑是最准确的选择,又由于我们链表不只是为了储存用户信息,还可能存储其他信息,故为了应对不同的查找,在List.h中定义查找函数,用过什么查找可以通过不同回调函数决定查找什么

List.h定义查找API

// 查找,等于data的数据,然后做处理
typedef bool (*CMPCALLBACK)(void*, void*);
void* list_find(List* plist, CMPCALLBACK cmp, void* data);

List.c实现

void* list_find(List* plist, CMPCALLBACK cmp, void* data)
{
	if (!isVailed(plist)) {
		printf("function is %s, plist is null\n", __FUNCTION__);
		return;
	}

	Node* cur = plist->front->next;

	while (cur) {
		if (cmp(cur->data, data)) {
			return cur->data;
		}
		cur = cur->next;
	}
}

实现对信息的修改

// 比较回调函数
bool user_id_cmp(void* v1, void* v2)
{
	User* u1 = (User*)v1;
	unsigned long long find = (unsigned long long)v2;
	if (u1->user_id == find) {
		return true;
	}
	return false;
}
void userManage_modify(UserManage* userm)
{
	unsigned long long find_userid;
	int modify_type = -1;
	printf("请输入你要修改的用户名ID >>: ");
	int res = scanf("%llu", &find_userid);
	if (res != 1) {
		return;
	}
	// 寻找对于的数据
	User* target_data = (User*)list_find(&userm->listManager, user_id_cmp, find_userid);
	if (!target_data) {
		printf("这个id不存在\n");
		return;
	}
	// 修改数据
	printf("请输入要修改的身份类型: ");
	res = scanf("%d", &modify_type);
	if (res != 1) {
		return;
	}
	target_data->type = modify_type;  // 修改类型
}

结果

在这里插入图片描述

修改成功,但是没有修改文件中的数据,这个后面我们再统一修改。

用户信息输入

userManage.c完成用户输入部分

void userManage_input(UserManage* userm)
{
	User* new_user = createUser();

	printf("请输入要创建用户的用户名ID>> ");
	int res = scanf("%llu", &new_user->user_id);
	if (res != 1) {
		return;
	}
	printf("请输入要创建用户的密码(8位数,数字+字母)>> ");
	res = scanf("%s", new_user->user_password);
	if (res != 1) {
		return;
	}
	printf("请输入要创建用户的用户类型(1:系统管理员, 2:图书管理员, 3:普通读者)>> ");
	res = scanf("%d", &new_user->type);

	// 插入最后
	list_pushBack(&userm->listManager, new_user);
}

结果

在这里插入图片描述

用户密码修改

用户密码修改,需要先登录用户,故先实现用户的登入,这里先模拟,后面在按模块封装,一步一步来。

  • 首先在userManage.h 和 userManage.c中添加检验是否登录成功的函数:
// userManage.h中
// 检查用户登录是否成功
bool check_user_login(UserManage* userm, unsigned long long user_id, const char* user_password);

// userManage.c中
// 检查用户是否能登录成功
static bool cmp_user(void* v1, void* v2)
{
	User* u1 = (User*)v1;
	User* u2 = (User*)v2;
	return (u1->user_id == u2->user_id) && (strcmp(u1->user_password, u2->user_password) == 0);
}
bool check_user_login(UserManage* userm, unsigned long long user_id, const char* user_password)
{
	User user;
	user.user_id = user_id;
	strcpy(user.user_password, user_password);
	// 查找
	User* res = list_find(&userm->listManager, cmp_user, &user);
	if (!res) {
		printf("用户名、密码输入错误\n");
		return false;
	}

	return true;
}
  • 接着,在“main.c”中模拟登录
int main()
{
	UserManage manage;
	userManage_init(&manage, "./data/user.txt");

	int countMax = 3;
	int curCount = 0;
	while (true)
	{
		unsigned long long user_id = -1;
		char passward[10] = { 0 };
		printf("请输入登录用户ID>> ");
		int res = scanf("%llu", &user_id);
		if (res <= 0) exit(0);
		printf("请输入登录的密码>> ");
		res = scanf("%s", passward);

		// 验证是否登录成功
		if (check_user_login(&manage, user_id, passward)) {   // 成功
			printf("login success~~\n");
			break;
		}
		else {			// 失败
			curCount++;
			if (curCount >= countMax) {
				printf("超过三次输入错误\n");
				exit(0);
			}
			printf("登录失败, 您还剩下%d次机会\n", countMax - curCount);
		}
	}

	// 开始管理
	userManage_operator(&manage);

	return 0;
}
  • 运行,登录输出结果如下

在这里插入图片描述

  • 这个时候可以修改密码了,在修改密码前,我们首先需要存储当下登录的用户信息
// 在** userManage.h ** 文件中添加用户
typedef struct UserManage {
	List listManager;				// 用户用链表存储
	User user;	// 保存当前登录用户信息,由于是单机的,故只有一个用户
}UserManage;

在登录成功后进行储存

bool check_user_login(UserManage* userm, unsigned long long user_id, const char* user_password)
{
	…………………………

	// 登陆成功,保存当下用户信息
	memcpy(&userm->user, res, sizeof(User));

	return true;
}
  • 最后就可以实现修改函数了
void userManage_modifyPassword(UserManage* userm)
{
	char password[10] = { 0 };
	printf("请输入要修改的密码>> ");
	int res = scanf("%s", password);
	if (res <= 0) {
		return;
	}

	// 查找
	User* user = list_find(&userm->listManager, cmp_user, &userm->user);
	strcpy(user->user_password, password);  // 修改密码
	printf("修改密码成功~~\n");
}

实现结果:

在这里插入图片描述

用户信息删除

通过上面的做法,这个就简单了,如下:

void userManage_delete(UserManage* userm)
{
	// 先不加权限
	unsigned long long user_id;
	printf("请输入要删除的用户ID>> ");
	int res = scanf("%llu", &user_id);
	if (res <= 0) {
		return;
	}

	// 查找
	User* user = list_find(&userm->listManager, user_id_cmp, user_id);
	if (!user) {
		printf("该用户ID不存在\n");
		return;
	}
	// 删除
	list_removeOne(&userm->listManager, user);
}

效果检查:

在这里插入图片描述

小结

这里,我们就完成第一个部分,不知道有多少人跟下来,大概功能基本实现了,后面就是不同功能之间的配合,权限之间的添加了。

7、框架的整理

main函数有些乱,我们可以进一步进行封装,封装比较简单,我就不一一讲解了,封装如下:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdbool.h>
#include <Windows.h>
#include "UserManager.h"
#include "Menu.h"

typedef struct ManageMent
{
	UserManage userManage;   // 用户管理
}ManageMent;

// 初始化
void management_init(ManageMent* m);
// 登录
void management_login(ManageMent* m);
// 操作
void management_run(ManageMent* m);

int main()
{
    // 测试
	ManageMent manage;

	management_init(&manage);
	management_run(&manage);

	return 0;
}

void management_init(ManageMent* m)
{
	userManage_init(&m->userManage, "./data/user.txt");
}

// 登录
void management_login(ManageMent* m)
{
	int countMax = 3;
	int curCount = 0;
	while (true)
	{
		unsigned long long user_id = -1;
		char passward[10] = { 0 };
		printf("请输入登录用户ID>> ");
		int res = scanf("%llu", &user_id);
		if (res <= 0) exit(0);
		printf("请输入登录的密码>> ");
		res = scanf("%s", passward);

		// 验证是否登录成功
		if (check_user_login(m, user_id, passward)) {   // 成功
			printf("login success~~\n");
			break;
		}
		else {			// 失败
			curCount++;
			if (curCount >= countMax) {
				printf("超过三次输入错误\n");
				exit(0);
			}
			printf("登录失败, 您还剩下%d次机会\n", countMax - curCount);
		}
	}
}
// 操作
void management_run(ManageMent* m)
{
	int op = welcomeMenu();
	if (op == 1)
	{
		management_login(m);
	}
	else
	{
		exit(0);
	}

	bool isQuit = false;
	while (!isQuit) {
		int op = mainMenu();
		switch (op)
		{
		case 1:			// 用户管理
			userManage_operator(&m->userManage.listManager);
			break;
		case 2:		  // 读者管理
			readerMenu();
			break;
		case 3:			// 图书管理
			managerMenu();
			break;
		case 4:			// 图书流通管理
			circulateMenu();
			break;
		case 5:			// 退出
			isQuit = true;
			break;
		default:
			printf("输入有误\n");
			break;
		}
		if (isQuit) {
			break;
		}
	}
}

part2

0、读者管理需求

读者管理子系统只有图书管理员使用,本模块应该包括读者信息输入、读者信息修改、读者信息删除、读者信息按名查询等功能。

读者管理菜单要求包括如下选项:

**********************************
		1.读者信息输入
		2.读者信息修改
		3.读者信息删除
		4.读者信息查询
		5.读者信息显示
		6.返回主菜单
**********************************

读者管理子系统设计道德读者信息文件要求如下表所示,其中读者号是学号或者教工号,教工可借书数为10本,学生可借书数为5本。图书管理员登陆时要讲该文件打开,将其内容读进来,形成一个单链表,推出系统时将单链表中的内容写回"读者信息文件"。

  • 读者信息文件
读者号读者名单位联系方式可借书数已借书数
1998017丁一网络中心13915082233105
2001021王二图书馆13145092236103
1998003张三计算机学院13745092237108
20172568李四软件学院1394509223953

1、读者管理框架搭建

读者信息

首先创建类,封装书籍信息,“Reader.h"和”Reader.c“,目录如下:

在这里插入图片描述

  • 定义实现**”Reader.h“**
#ifndef READER_H_
#define READER_H_

typedef struct Reader {
	int reader_record_number;			//记录号
	int reader_number;				//书号
	char reader_name[20];				//书名
	char reader_author[20];			//作者
	char reader_publisher[20];		//出版社
	int reader_collection_count;		//藏书量
	int reader_borrow_count;			//图书借出数量
}Reader;

// 创建一个没用初始化书籍
Reader* createEmptyReader();
// 打印书籍信息
void printReaderMess(Reader* book);

#endif // !READER_H_
  • 实现**“Reader.c”**d的功能
#include "Reader.h"

#include <stdio.h>
#include <stdlib.h>

Reader* createEmptyReader()
{
	Reader* new_reader = calloc(1, sizeof(Reader));
	if (!new_reader) {
		printf("function is %s, calloc failed\n", __FUNCTION__);
		return NULL;
	}

	return new_reader;
}

void printReaderMess(Reader* book)
{
	printf("%-10d %-10s %-15s %-15s %-10d %d\n", book->reader_number, book->reader_name, book->reader_company, book->reader_community, book->reader_collection_count, book->reader_borrow_count);
}

读者管理

读者框架搭建其实和用户大差不差。

  • 首先创建类,进行封装,创建**"ReaderManage.h"“ReaderManage.c”**,创建目录如下:

在这里插入图片描述

  • "ReaderManage.h"搭建
#ifndef BOOKMANAGER_H_
#define BOOKMANAGER_H_

#include "List.h"
#include "Book.h"

typedef struct ReaderManager
{
	List bookManageList;
}ReaderManager;

// 读者信息初始化
void readerManage_init(ReaderManager* readerm, const char* filename);
// 读者信息加载
void readerManage_load_from_file(ReaderManager* readerm, const char* filename);


// 读者管理run
void readerManage_operate(ReaderManager* readerm);
// 读者信息输入
void readerManage_imput(ReaderManager* readerm);
// 读者信息修改
void readerManage_modify(ReaderManager* readerm);
// 读者信息删除
void readerManage_remove(ReaderManager* readerm);
// 读者信息查询
void readerManage_search(ReaderManager* readerm);
// 读者信息显示
void readerManage_show(ReaderManager* readerm);


#endif // !BOOKMANAGER_H_
  • "ReaderManage.c"的实现
#include "ReaderManager.h"


void readerManage_init(ReaderManager* readerm, const char* filename)
{

}

void readerManage_load_from_file(ReaderManager* readerm, const char* filename)
{

}

void readerManage_operate(ReaderManager* readerm)
{

}

void readerManage_imput(ReaderManager* readerm)
{

}

void readerManage_modify(ReaderManager* readerm)
{

}

void readerManage_remove(ReaderManager* readerm)
{

}

void readerManage_search(ReaderManager* readerm)
{

}

void readerManage_show(ReaderManager* readerm)
{

}

2、读者功能的实现

从文件加载数据

  • 首先创建文件,存放读者信息,文件目录如下:

在这里插入图片描述

  • 显示功能的实现

ReaderManage.h

// 读者信息初始化
void readerManage_init(ReaderManager* readerm, const char* filename);
// 读者信息加载
void readerManage_load_from_file(ReaderManager* readerm, const char* filename);

ReaderManage.c

void readerManage_init(ReaderManager* readerm, const char* filename)
{
	// 链表初始化
	list_init(&readerm->bookManageList);

	// 加载数据
	readerManage_load_from_file(&readerm->bookManageList, filename);
}

void readerManage_load_from_file(ReaderManager* readerm, const char* filename)
{
	FILE* fp = fopen(filename, "r");
	if (!fp) {
		printf("function is %s, file open failed\n", __FUNCTION__);
		return;
	}
	// 读取头
	char buff[BUFSIZ] = { 0 };
	fgets(buff, BUFSIZ, fp);

	// 读取内容
	while (!feof(fp)) {
		memset(buff, 0, sizeof(buff));   // 清空

		// 读取一条数据
		fgets(buff, BUFSIZ, fp);

		// 创建
		Reader* r = createEmptyReader();

		// 赋值
		int res = sscanf(buff, "%d %s %s %s %d %d", &r->reader_number, r->reader_name, r->reader_company, r->reader_community, &r->reader_collection_count, &r->reader_borrow_count);
		if (res <= 0) {
			return;
		}

		// 加入链表
		list_pushBack(&readerm->bookManageList, r);
	}
    fclose(fp);
}
  • 测试,这一次我们通调试的方法进行查看,是否读取成功,打入两个断点,调试查看

测试前,需要对main创建读者管理,并对其初始化,代码如下:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdbool.h>
#include <Windows.h>
#include "UserManager.h"
#include "ReaderManager.h"
#include "Menu.h"

typedef struct ManageMent
{
	UserManage userManage;			// 用户管理
	ReaderManager readerManage;		// 读者管理
}ManageMent;

// 初始化
void management_init(ManageMent* m);
// 登录
void management_login(ManageMent* m);
// 操作
void management_run(ManageMent* m);

int main()
{
	ManageMent manage;

	management_init(&manage);
	management_run(&manage);

	return 0;
}

void management_init(ManageMent* m)
{
	userManage_init(&m->userManage, "./data/user.txt");
	readerManage_init(&m->readerManage, "./data/reader.txt");
}

// 登录
void management_login(ManageMent* m)
{
	int countMax = 3;
	int curCount = 0;
	while (true)
	{
		unsigned long long user_id = -1;
		char passward[10] = { 0 };
		printf("请输入登录用户ID>> ");
		int res = scanf("%llu", &user_id);
		if (res <= 0) exit(0);
		printf("请输入登录的密码>> ");
		res = scanf("%s", passward);

		// 验证是否登录成功
		if (check_user_login(m, user_id, passward)) {   // 成功
			printf("login success~~\n");
			break;
		}
		else {			// 失败
			curCount++;
			if (curCount >= countMax) {
				printf("超过三次输入错误\n");
				exit(0);
			}
			printf("登录失败, 您还剩下%d次机会\n", countMax - curCount);
		}
	}
}
// 操作
void management_run(ManageMent* m)
{
	int op = welcomeMenu();
	if (op == 1)
	{
		management_login(m);
	}
	else
	{
		exit(0);
	}

	bool isQuit = false;
	while (!isQuit) {
		int op = mainMenu();
		switch (op)
		{
		case 1:			// 用户管理
			userManage_operator(&m->userManage.listManager);
			break;
		case 2:		  // 读者管理
			readerManage_operate(&m->readerManage.bookManageList);
			break;
		case 3:			// 图书管理
			managerMenu();
			break;
		case 4:			// 图书流通管理
			circulateMenu();
			break;
		case 5:			// 退出
			isQuit = true;
			break;
		default:
			printf("输入有误\n");
			break;
		}
		if (isQuit) {
			break;
		}
	}
}

测试结果,断点调试

在这里插入图片描述

在这里插入图片描述

读者信息显示

上面方法可以学习vs的调试能力,但是具体要看有没有问题,还需要把他内容全部输出出来,故在这里我们先实现信息展示功能,还是用回调函数进行,代码如下:

void readerManage_show(ReaderManager* readerm)
{
	// 标题
	printf("%-10s %-10s %-15s %-15s %-10s %-10s\n", "读者号", "读者名", "单位", "联系方式", "可借书数量", "已借书数量");
	// 读者信息打印
	list_transform(&readerm->bookManageList, printReaderMess);
}

输出结果如下:

在这里插入图片描述

读者信息输入

void readerManage_input(ReaderManager* readerm)
{
	Reader* reader = createEmptyReader();
	printf("请输入读者号>> ");
	int res = scanf("%d", &reader->reader_number);
	printf("请输入读者名>> ");
	res = scanf("%s", reader->reader_name);
	printf("请输入单位>> ");
	res = scanf("%s", reader->reader_company);
	printf("请输入读者联系方式>> ");
	res = scanf("%s", reader->reader_community);
	printf("请输入读者可借书数量>> ");
	res = scanf("%d", &reader->reader_borrow_count);

	// 添加用户
	list_pushBack(&readerm->bookManageList, reader);
	printf("添加用户成功~~\n");
}

结果测试:

在这里插入图片描述

读者信息查询

要求:通过读者号查询,其实这一段也很简单,逻辑简单。

void readerManage_search(ReaderManager* readerm)
{
	int reader_num = -1;
	printf("请输入你要查询读者的读者号>> ");
	int res = scanf("%d", &reader_num);
	if (res <= 0) {
		return;
	}

	// 查找
	Reader* reader = list_find(&readerm->bookManageList, cmp_reader_num, reader_num);
	if (!reader) {
		printf("不存在该读者信息\n");
		return;
	}

	// 显示
	printf("%-10s %-10s %-15s %-15s %-10s %-10s\n", "读者号", "读者名", "单位", "联系方式", "可借书数量", "已借书数量");   // 这一句可以封装,因为不止一次使用了
	printReaderMess(reader);
}

运行结果:

在这里插入图片描述

读者信息删除

这个和Part1的部分一样。

void readerManage_remove(ReaderManager* readerm)
{
	int reader_num = -1;
	printf("请输入你要删除读者的读者号>> ");
	int res = scanf("%d", &reader_num);
	if (res <= 0) {
		return;
	}

	// 查询
	Reader* reader = list_find(&readerm->bookManageList, cmp_reader_num, reader_num);
	if (!reader) {
		printf("不存在该读者的信息\n");
	}  // 这一部分可以封装,多次使用,这里为了初学的人好理解,就不封装了

	// 移除
	list_removeOne(&readerm->bookManageList, reader);
}

结果检测:

在这里插入图片描述

读者信息修改

这个需要根据实际业务,这里修改联系方式和可借书的数量。

代码比较冗余,本章小结中会提供一些改进方案,大家可以尝试区优化代码,也不难。

void readerManage_remove(ReaderManager* readerm)
{
	int reader_num = -1;
	printf("请输入你要删除读者的读者号>> ");
	int res = scanf("%d", &reader_num);
	if (res <= 0) {
		return;
	}

	// 查询
	Reader* reader = list_find(&readerm->bookManageList, cmp_reader_num, reader_num);
	if (!reader) {
		printf("不存在该读者的信息\n");
	}  // 这一部分可以封装,多次使用,这里为了初学的人好理解,就不封装了

	// 移除
	list_removeOne(&readerm->bookManageList, reader);
}

测试结果:

在这里插入图片描述

小结

  • 这一部分其实和Part1恶的用户管理很相似,功能很多一样。
  • 很多部分代码重复,比较冗余,比如说:scanf返回值处理,list_find查找与数据修改,都可以用函数封装,当然有一部分也可以用宏去简化,难度不大,但是效果会好很多,请大家在完成上面部分的时候尝试简化代码。

Part3

0、图书管理需求

本模块至少应包括图书信息输入、图书信息修改、图书信息查询、汇总统计等功能。其它功能(如图书订阅、图书编目、新书通报等功能)可根据自身情况酌情实现。“图书管理员”可以使用本模块中的全部功能,“普通读者”和“系统管理员”只能使用其中的图书信息查询和图书数据统计功能(功能3和功能4),当普通用户选择其他功能时应该告知不能使用。

图书管理菜单至少要求包括如下选项:

**********************************
		1.图书信息输入
		2.图书信息修改
		3.图书信息查询
		4.汇总统计
		5.返回主菜单
**********************************

在“2.图书信息修改”中,只要求提供对“藏书量”和“借出数"的修改功能。

如果在“图书管理”菜单中选择了"3.图书信息管理",系统应提示如下子菜单。

图书信息查询菜单应包含如下选项:

**********************************
		1.按书号查询
		2.按书名查询
		3.按作者查询
		4.按出版社查询
		5.返回主菜单
**********************************

“图书管理子系统”设计“图书主文件”和书名索引表、作者索引表、出版社索引表三个次关键字索引表,分别如下面一系列图所示。系统开始运行时要将上述文件打开,将其内容读进来,分别存入四个一维数组中,在系统运行结束时再分别将四个一维数组中的内容写回相应文件。

  • 图书主文件
记录号书号书名作者出版社藏书量借出数指针1指针2指针3
11021数据库杨艳人民邮电108000
21014数据结构赵鹏高等教育97000
31106操作系统金虎人民邮电86001
41108数据结构高扬清华大学75200
51203程序设计杨艳高等教育94012
62105数据库金虎清华大学73134
71012数据结构杨艳人民邮电82453
80109程序设计赵鹏清华大学91526
  • 书名次关键字索引表
书名链头指针长度
数据库62
数据结构73
操作系统31
程序设计82
  • 作者次关键字索引表
作者链头指针长度
杨艳73
赵鹏82
金虎62
高扬41
  • 出版社次关键字索引表
出版社链头指针长度
人民邮电73
高等教育52
清华大学83

1、图书模块搭建

图书信息

  • 首先创建“Book.h”和“Book.c”文件

在这里插入图片描述

  • “Book.h”定义

注意:p1、p2、p3是索引,属于后面进阶的内容,先不用管,先写上。

#ifndef BOOK_H_
#define BOOK_H_

typedef struct Book {
	int reacod_num;		// 记录号
	int book_num;		// 书号码
	char book_name[20];		// 书名
	char book_author[20];	// 作者
	char book_publish[20];	// 出版社
	int book_count;			// 藏书量
	int book_borrow_cnt;	// 借出数
	int p1;
	int p2;
	int p3;
}Book;

// 创建一个空书
Book* createEmptyBook();
// 通过字符串创建书籍
Book* createBookFromString(const char* str);
// 打印书籍信息
void printBookMess(Book* book);

#endif // !BOOK_H_
  • “Book.h”的实现
#include "Book.h"

#include <stdio.h>
#include <stdlib.h>

Book* createEmptyBook()
{
	Book* new_book = calloc(1, sizeof(Book));
	if (!new_book) {
		printf("function is %s, calloc failed\n", __FUNCTION__);
		return NULL;
	}
	return new_book;
}

Book* createBookFromString(const char* str)
{
	Book* book = calloc(1, sizeof(Book));
	if (!book) {
		printf("function is %s, calloc failed\n", __FUNCTION__);
		return NULL;
	}  // 可以封装成宏,建议尝试简化代码

	int res = sscanf(str, "%d %d %s %s %s %d %d %d %d %d", &book->reacod_num, &book->book_num, book->book_name, book->book_author, book->book_publish, &book->book_count, &book->book_borrow_cnt, &book->p1, &book->p2, &book->p3);
	if (res <= 0) {
		printf("function is %s failed\n", __FUNCTION__);
		free(book);
		return NULL;
	}

	return book;
}

void printBookMess(Book* book)
{
	printf("%-8d %-5d %-10s %-10s %-10s %-6d %-3d", book->reacod_num, book->book_num, book->book_name, book->book_author, book->book_publish, book->book_count, book->book_borrow_cnt);
}

框架的搭建

  • 首先创建管理文件,“BookManager.c”和“BookManager.h”

在这里插入图片描述

  • 初始化API定义与实现

**“BookManage.h”**定义

#ifndef BOOK_MANAGER_H_
#define BOOK_MANAGER_H_

#include "SeqList.h"

typedef struct BookManage
{
	SeqList bookManages;   // 图书信息存储
}BookManage;

// 图书信息初始化
void bookManage_init(BookManage* bookm, const char* filename);
// 图书信息加载,从文件
void bookManage_load_from_file(BookManage* bookm, const char* filename);

#endif // !BOOK_MANAGER_H_

**“BookManage.c”**实现

void bookManage_init(BookManage* bookm, const char* filename)
{
	// 顺序表初始化
	SeqList_init(&bookm->bookManages);
	// 加载数据
	bookManage_load_from_file(&bookm->bookManages, filename);
}

void bookManage_load_from_file(BookManage* bookm, const char* filename)
{
	FILE* fp = fopen(filename, "r");
	if (!fp) {
		printf("function is %s, file open failed\n", __FUNCTION__);
		return;
	}

	// 定义缓冲区
	char buff[BUFSIZ] = { 0 };
	// 读取文件头
	fgets(buff, BUFSIZ, fp);

	while (!feof(fp)) {
		memset(buff, 0, sizeof(buff));

		fgets(buff, BUFSIZ, fp);

		// 插入
		SeqList_pushBack(&bookm->bookManages, createBookFromString(buff));
	}

	fclose(fp);
}
  • 由于第三点,图书信息的查询,是另外一个表,故这里内容比较多,可以细看。

**“BookManage.h”**定义

// 继续添加
// 图书信息操作
void bookManage_operator(BookManage* bookm);

// 图书信息输入
void bookManage_input(BookManage* bookm);
// 图书信息修改
void bookManage_modify(BookManage* bookm);
// 图书信息查询
void bookManage_search(BookManage* bookm);
// 图书信息汇总
void bookManage_collect(BookManage* bookm);

//-----------  图书查询部分API
// 按照书号查询
void bookManage_search_bookNo(BookManage* bookm);
// 按照书名查询
void bookManage_search_bookNmae(BookManage* bookm);
// 按照作者查询
void bookManage_search_authorName(BookManage* bookm);
// 按照出版社查询
void bookManage_search_publish(BookManage* bookm);

**“BookManage.cpp”**定义

void bookManage_operator(BookManage* bookm)
{
	bool isQuit = false;
	while (!isQuit) {
		int op = managerMenu();
		switch (op)
		{
		case 1:   // 图书信息输入
			bookManage_input(bookm);
			break;
		case 2:  // 图书信息修改
			bookManage_modify(bookm);
			break;
		case 3:  // 图书信息查询
			bookManage_search(bookm);
			break;
		case 4:   // 图书信息汇总
			bookManage_collect(bookm);
			break;
		case 0:  // 退出
			printf("欢迎下次使用\n");
			isQuit = true;
			break;
		}
		if (isQuit) {
			break;
		}
	}
}

void bookManage_input(BookManage* bookm)
{
}

void bookManage_modify(BookManage* bookm)
{
}

void bookManage_search(BookManage* bookm)
{
	bool isStop = false;
	while (true) {
		int op = searchMenu();
		switch (op)
		{
		case 1:   // 按书号查询
			bookManage_search_bookNo(bookm);
			break;
		case 2:  // 按书名查询
			bookManage_search_bookNmae(bookm);
			break;
		case 3:  // 按作者查询
			bookManage_search_authorName(bookm);
			break;
		case 4:  // 按出版社查询
			bookManage_search_publish(bookm);
			break;
		case 0:  // 退出
			printf("欢迎下次使用\n");
			isStop = true;
			break;
		}
		if (isStop) {
			break;
		}
	}
}

void bookManage_collect(BookManage* bookm)
{

}

//---------------------------------------
void bookManage_search_bookNo(BookManage* bookm)
{

}

void bookManage_search_bookNmae(BookManage* bookm)
{

}

void bookManage_search_authorName(BookManage* bookm)
{

}

void bookManage_search_publish(BookManage* bookm)
{

}
  • 测试,看是否能实现菜单的切换

在“main.c"中添加图书功能,实现逻辑很简单,就是在初始化,run中修改添加,看代码肯容易看懂修改位置,代码如下:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdbool.h>
#include <Windows.h>
#include "UserManager.h"
#include "ReaderManager.h"
#include "BookManager.h"
#include "Menu.h"

typedef struct ManageMent
{
	UserManage userManage;			// 用户管理
	ReaderManager readerManage;		// 读者管理
	BookManage bookManage;			// 图书管理
}ManageMent;

// 初始化
void management_init(ManageMent* m);
// 登录
void management_login(ManageMent* m);
// 操作
void management_run(ManageMent* m);

int main()
{
	ManageMent manage;

	management_init(&manage);
	management_run(&manage);

	return 0;
}

void management_init(ManageMent* m)
{
	userManage_init(&m->userManage, "./data/user.txt");
	readerManage_init(&m->readerManage, "./data/reader.txt");
	bookManage_init(&m->bookManage, "./data/book/books.txt");
}

// 登录
void management_login(ManageMent* m)
{
	int countMax = 3;
	int curCount = 0;
	while (true)
	{
		unsigned long long user_id = -1;
		char passward[10] = { 0 };
		printf("请输入登录用户ID>> ");
		int res = scanf("%llu", &user_id);
		if (res <= 0) exit(0);
		printf("请输入登录的密码>> ");
		res = scanf("%s", passward);

		// 验证是否登录成功
		if (check_user_login(m, user_id, passward)) {   // 成功
			printf("login success~~\n");
			break;
		}
		else {			// 失败
			curCount++;
			if (curCount >= countMax) {
				printf("超过三次输入错误\n");
				exit(0);
			}
			printf("登录失败, 您还剩下%d次机会\n", countMax - curCount);
		}
	}
}
// 操作
void management_run(ManageMent* m)
{
	int op = welcomeMenu();
	if (op == 1)
	{
		management_login(m);
	}
	else
	{
		exit(0);
	}

	bool isQuit = false;
	while (!isQuit) {
		int op = mainMenu();
		switch (op)
		{
		case 1:			// 用户管理
			userManage_operator(&m->userManage.listManager);
			break;
		case 2:		  // 读者管理
			readerManage_operate(&m->readerManage.bookManageList);
			break;
		case 3:			// 图书管理
			bookManage_operator(&m->bookManage.bookManages);
			break;
		case 4:			// 图书流通管理
			circulateMenu();
			break;
		case 5:			// 退出
			isQuit = true;
			break;
		default:
			printf("输入有误\n");
			break;
		}
		if (isQuit) {
			break;
		}
	}
}
  • 测试效果

在这里插入图片描述

2、图书功能实现

这一部分功能的实现,和上面逻辑上是一样的,但是使用的数据结构不同,这里使用顺序表进行存储,并且,本人给初学者提供了优化地方,这次代码实现比较冗余,请大家在做的时候进行代码优化,减少代码的冗余

图书信息输入实现

void bookManage_input(BookManage* bookm)
{
	Book* book = createEmptyBook();
	printf("请输入(书号、书名、作者、出版社、藏书量)>>");
	int res = scanf("%d %s %s %s %d", &book->book_num, book->book_name, book->book_author, book->book_publish, &book->book_count);
	if (res != 5) {
		return;
	}

	// 插入
	SeqList_pushBack(&bookm->bookManages, book);

	printf("添加图书成功~~\n");
}

这个不好检测,需要等写完查询信息的时候一起测试。

图书信息修改实现

void bookManage_modify(BookManage* bookm)
{
	int book_n = -1;
	printf("请输入你要修改图书的图书号>>");
	int res = scanf("%d", &book_n);
	if (res <= 0) {
		return;
	}

	// 查找
	for (int i = 0; i < bookm->bookManages.size; i++) {
		Book* tmp = (Book*)bookm->bookManages.data[i];
		if (book_n == tmp->book_num) {
			printf("请输入(藏书量、借出数)>");
			res = scanf("%d %d", &tmp->book_count, &tmp->book_borrow_cnt);
			if (res != 2) {
				return;
			}
			printf("修改图书成功~~\n");
		}
	}
}

这个不好检测,需要等写完查询信息的时候一起测试。

图书查询功能实现

按书号查询
void bookManage_search_bookNo(BookManage* bookm)
{
	int book_n = -1;
	printf("请输入你要查询的图书号>>");
	int res = scanf("%d", &book_n);
	if (res <= 0) {
		return;
	}

	// 查找, 可以进一步封装,大家可以思考,尝试,我这里仅仅实现功能
	for (int i = 0; i < bookm->bookManages.size; i++) {
		Book* tmp = (Book*)bookm->bookManages.data[i];
		if (book_n == tmp->book_num) {
			printf("%-8s %-5s %-10s %-10s %-10s %-6s %s\n", "记录号", "书号", "书名", "作者", "出版社", "藏书量", "借出数");
			printBookMess(tmp);  // 打印
            break;
		}
	}
}

测试,结合输入、修改一起测试。

在这里插入图片描述

按书名查询

查询方式逻辑很相似,大家可以进一步封装。

void bookManage_search_bookNmae(BookManage* bookm)
{
	// 很多相同,可以进行封装
	char bookName[20] = { 0 };
	printf("请输入你要查询的作者>>");
	int res = scanf("%s", bookName);
	if (res <= 0) {
		return;
	}

	printf("%-8s %-5s %-10s %-10s %-10s %-6s %s\n", "记录号", "书号", "书名", "作者", "出版社", "藏书量", "借出数");  // 可以封装

	bool isFind = false;
	// 查找, 可以进一步封装,大家可以思考,尝试,我这里仅仅实现功能
	for (int i = 0; i < bookm->bookManages.size; i++) {
		Book* tmp = (Book*)bookm->bookManages.data[i];
		if (strcmp(tmp->book_name, bookName) == 0) {
			printBookMess(tmp);
			isFind = true;
		}
	}

	if (!isFind)
		printf("不存在该图书信息~~\n");

}

在这里插入图片描述

按作者名查询

查询方式逻辑很相似,大家可以进一步封装。

void bookManage_search_authorName(BookManage* bookm)
{
	// 很多相同,可以进行封装
	char author[20] = { 0 };
	printf("请输入你要查询的作者>>");
	int res = scanf("%s", author);
	if (res <= 0) {
		return;
	}

	printf("%-8s %-5s %-10s %-10s %-10s %-6s %s\n", "记录号", "书号", "书名", "作者", "出版社", "藏书量", "借出数");

	bool isFind = false;
	// 查找, 可以进一步封装,大家可以思考,尝试,我这里仅仅实现功能
	for (int i = 0; i < bookm->bookManages.size; i++) {
		Book* tmp = (Book*)bookm->bookManages.data[i];
		if (strcmp(tmp->book_author, author) == 0) {
			printBookMess(tmp);
			isFind = true;
		}
	}

	if (!isFind)
		printf("不存在该图书信息~~\n");

}

在这里插入图片描述

按出版社查询

查询方式逻辑很相似,大家可以进一步封装。

void bookManage_search_publish(BookManage* bookm)
{
	// 很多相同,可以进行封装
	char publish[20] = { 0 };
	printf("请输入你要查询的作者>>");
	int res = scanf("%s", publish);
	if (res <= 0) {
		return;
	}

	printf("%-8s %-5s %-10s %-10s %-10s %-6s %s\n", "记录号", "书号", "书名", "作者", "出版社", "藏书量", "借出数");  // 可以封装

	bool isFind = false;
	// 查找, 可以进一步封装,大家可以思考,尝试,我这里仅仅实现功能
	for (int i = 0; i < bookm->bookManages.size; i++) {
		Book* tmp = (Book*)bookm->bookManages.data[i];
		if (strcmp(tmp->book_publish, publish) == 0) {
			printBookMess(tmp);
			isFind = true;
		}
	}

	if (!isFind)
		printf("不存在该图书信息~~\n");
}

在这里插入图片描述

汇总功能实现

void bookManage_collect(BookManage* bookm)
{
	// 这里只统计可借书的数量
	int sum = 0;
	for (int i = 0; i < bookm->bookManages.size; i++) {
		Book* tmp = (Book*)bookm->bookManages.data[i];
		sum += tmp->book_count;
	}
	printf("还剩下【%d】本书可借\n", sum);
}

测试结果:

在这里插入图片描述

小结

  • 这部分逻辑上和Part1和Part2功能很相似,跟到这里的朋友应该很容易做出来了;
  • 强烈建议:思考代码优化,简化代码,对初学者来说能学到不少东西(提示:函数、宏)。

Part4

0、图书流通子系统需求

至少应该包括借书处理和还书处理功能。其他功能(如预约处理、语气处理等功能)可根据自身情况酌情实现。本模块由“图书管理员”使用,普通用户只能使用预约处理功能(如果有预约功能)。

“图书流通管理”菜单至少要求包括如下几项:

**********************************
		1.借书处理
		2.还书处理
		5.返回主菜单
**********************************

“图书流通管理子系统”涉及“还书信息文件“,如下表所示,系统开始运行时要将文件打开,将其内容读进来,建立单链表,在系统运行结束时再将该单链表中的内容写回响应文件。本模块运行时还涉及”读者信息文件“和"图书主文件"。

  • 借还书信息文件
读者号书号借书日期还书日备注
199801710212022/03/212022/04/15
200102110142022/03/25过期未还
198800311062022/03/282022/04/16
2017256811082022/06/092022/06/22

1、图书流通信息

  • 首先创建**.h和.c文件**,如图:

在这里插入图片描述

  • “circulate.h”
#ifndef CIRCULATE_H_
#define CIRCULATE_H_

typedef struct Circulate
{
	unsigned long long reader_num;   // 读者号
	int book_num;					// 书号
	char borrow_date[20];			// 借书日期
	char return_data[20];			// 还书日
	char comment[20];				// 备注
}Circulate;

// 创建空对象
Circulate* createEmptyCirulate();
// 通过字符串创建
Circulate* createCircuateFromString(const char* str);
// 打印
void printCirulateMess(Circulate* data);

#endif // !CIRCULATE_H_
  • “circulate.c”
#include "Circulate.h"

#include <stdio.h>
#include <stdlib.h>

Circulate* createEmptyCirulate()
{
	Circulate* _new = calloc(1, sizeof(Circulate));
	if (!_new) {
		printf("function is %s, calloc failed\n", __FUNCTION__);
		return NULL;
	}
	return _new;
}

Circulate* createCircuateFromString(const char* str)
{
	Circulate* _new = calloc(1, sizeof(Circulate));
	if (!_new) {
		printf("function is %s, calloc failed\n", __FUNCTION__);
		return NULL;
	}

	int res = sscanf(str, "%llu %d %s %s %s", &_new->reader_num, &_new->book_num, _new->borrow_date, _new->return_data, _new->comment);
	if (res <= 0) {
		free(_new);
		return NULL;
	}

	return _new;
}

void printCirulateMess(Circulate* data)
{
	printf("%-8llu %-8d %-10s %-10s %s\n", data->reader_num, data->book_num, data->borrow_date, data->return_data, data->comment);
}

2、框架搭建

这个部分有一个难点,就是借书和还书的时候需要更新图书状态,图书管理在Part3部分,故需要存储图书管理的指针,具体如下:

  • 首先创建文件,如下:

在这里插入图片描述

  • "CirculateManage.h"搭建
#ifndef CIRCULATEMANAGE_H_
#define CIRCULATEMANAGE_H_

#include "List.h"
#include "BookManager.h"

typedef struct CirculateManage {
	List cirManage;				//借书还书列表
	BookManage* bookManage;		//图书管理,需要联通借书、还书信息
}CirculateManage;

// 初始化
void circulateManage_init(CirculateManage* cirm, const char* filename, BookManage* bookm);
// 加载数据
void circulateManage_load(CirculateManage* cirm, const char* filename);

// 流通操作
void circulateManage_operator(CirculateManage* cirm);


// 借书处理
void circulateManage_borrow(CirculateManage* cirm);
// 还书处理
void circulateManage_return(CirculateManage* cirm);

#endif // !CIRCULATEMANAGE_H_
  • "CirculateManage.c"搭建
#include "CirculateManage.h"
#include "Circulate.h"
#include "Menu.h"
#include "Book.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void circulateManage_init(CirculateManage* cirm, const char* filename, BookManage* bookm)
{

}

void circulateManage_load(CirculateManage* cirm, const char* filename)
{
	
}

void circulateManage_operator(CirculateManage* cirm)
{
	
}

void circulateManage_borrow(CirculateManage* cirm)
{
	
}

void circulateManage_return(CirculateManage* cirm)
{
	
}

3、功能实现

这个功能比较少,但是难点在于如何实现与图书馆里的互通,这里就不再分点了,代码不多。

#include "CirculateManage.h"
#include "Circulate.h"
#include "Menu.h"
#include "Book.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void circulateManage_init(CirculateManage* cirm, const char* filename, BookManage* bookm)
{
	list_init(&cirm->cirManage);  // 链表初始化
	cirm->bookManage = bookm;

	// 加载
	circulateManage_load(cirm, filename);
}

void circulateManage_load(CirculateManage* cirm, const char* filename)
{
	FILE* fp = fopen(filename, "r");
	if (!fp) {
		perror("file open failed\n");
		return;
	}

	char buff[BUFSIZ] = { 0 };
	// 读取头
	fgets(buff, BUFSIZ, fp);

	// 读取内容,添加进链表
	while (!feof(fp)) {
		memset(buff, 0, sizeof(buff));

		fgets(buff, BUFSIZ, fp);

		list_pushBack(&cirm->cirManage, createCircuateFromString(buff));
	}
	fclose(fp);
}

void circulateManage_operator(CirculateManage* cirm)
{
	bool isDone = false;
	while (!isDone) {
		int op = circulateMenu();
		switch (op)
		{
		case 1:   // 借书处理
			circulateManage_borrow(cirm);
			break;
		case 2:  // 还书处理
			circulateManage_return(cirm);
			break;
		case 0:   // 返回
			printf("欢迎下次使用~~\n");
			isDone = true;
			break;
		}
		if (isDone) {
			break;
		}
	}
}

void circulateManage_borrow(CirculateManage* cirm)
{
	// 创建
	Circulate* _new = createEmptyCirulate();

	printf("请输入借书的(读者号、书号、借书日期)>>");
	int res = scanf("%llu %d %s", &_new->reader_num, &_new->book_num, _new->borrow_date);
	if (res != 3) {
		return;
	}

	// 插入信息
	list_pushBack(&cirm->cirManage, _new);

	// 图书借书数量+1
	for (int i = 0; i < cirm->bookManage->bookManages.size; i++) {
		Book* book = (Book*)cirm->bookManage->bookManages.data[i];
		if (book->book_num == _new->book_num) {
			book->book_borrow_cnt++;
			break;
		}
	}

	printf("插入借书信息成功~~\n");
}


bool cmp_cir(Circulate* v1, Circulate* v2)
{
	return (v1->reader_num == v2->reader_num) && (v1->book_num == v2->book_num);
}
void circulateManage_return(CirculateManage* cirm)
{
	Circulate cir;
	printf("请输入还书的(读者号、书号)>");
	int res = scanf("%llu %d", &cir.reader_num, &cir.book_num);
	if (res <= 0) {
		return;
	}

	// 查找是否有该书记录
	Circulate* pList = list_find(&cirm->cirManage, cmp_cir, &cir);
	if (!pList) {
		printf("不存在该书的的借书消息\n");
		return;
	}

	// 还书,移除链表
	list_removeOne(&cirm->cirManage, pList);

	// 还书,借书量-1
	for (int i = 0; i < cirm->bookManage->bookManages.size; i++) {
		Book* book = (Book*)cirm->bookManage->bookManages.data[i];
		if (book->book_num == cir.book_num) {
			book->book_borrow_cnt--;
			break;
		}
	}

	printf("还书信息成功~~\n");
}

4、测试

  • 借书:

在这里插入图片描述

  • 还书:

在这里插入图片描述

Part5

0、权限需求

普通读者”只能选“用户管理”功能中“用户密码修改”子功能和“图书管理”子系统中“图书信息查询”子功能;

"图书管理员"可以选择“读者管理”、“图书管理”、“图书流通管理”功能;

系统管理员”只能选择“用户管理”和"图书管理"子系统中"图书信息查询子功能"

需要根据用户类型显示相应的菜单,请用户选择功能,或者用同一的主菜单,但不允许用户使用没有相应权限的功能。

当退出系统是,需要将各个链表和数组中的内容写回相应的文件。

1、权限分析与准备

在这里插入图片描述

  • 在“User.h”和“User.c”中定义判断身份函数,如下:
// User.h
// 判断你普通用户
bool user_isCommon(User* user);
// 判断系统管理用户
bool user_isSysAdmin(User* user);
// 判断图书管理员
bool user_isBookAdmin(User* user);

// User.c
bool user_isCommon(User* user)
{
	return user->type == Comon;
}

bool user_isSysAdmin(User* user)
{
	return user->type == SYS_MANAGER;
}

bool user_isBookAdmin(User* user)
{
	return user->type == BOOK_MANAGER;
}

这里又有一个难点,就是怎么在不同的管理功能模块中,获取用户类型呢?

可以在初始化中传递相应的参数,但是这里为了简单,只实现一个简单的方法,采用全局变量的形势,在UserManage.c中前面定义:

User* get_user = NULL;   // 全局变量,指针,在用户登录上使用
// 在登录模块,登录成功后赋值类型
get_user = res;   
get_user->type == res->type;   // 保存用户类型

那该如何在不同文件中使用呢?

// 在不同模块(文件)使用只需要在使用的地方前加上
extern User* get_user;

2、功能实现

图书流通

图书流通模块最简单,只允许图书管理员使用,故先实现这个功能。

  • 首先在“CirculateManage.c”中定义静态函数
static bool canOp()
{
	extern User* get_user;
	if (!user_isBookAdmin(get_user)) {
		printf("您没有此权限\n");
		return false;
	}
	return true;
}
  • 分别在借书和还书前判断有没有权限
void circulateManage_borrow(CirculateManage* cirm)
{
	// 权限判断
	if (!canOp()) {
		return;
	}

	……………………………………………………………………
}

void circulateManage_return(CirculateManage* cirm)
{
	// 权限判断
	if (canOp()) {
		return;
	}
	
    ………………………………
}
  • 测试

在这里插入图片描述

图书管理

  • 这部分除了查询外,其他只有图书管理员有权限
// 定义判断函数,
static bool canOp()
{
	extern User* get_user;
	if (!user_isBookAdmin(get_user)) {
		printf("您没有权限~~\n");
		return false;
	}
	return true;
}
  • 后面分别在各个功能前加上判断,如下:
// 判断
if (!canOp()) {
	return;
}
  • 测试:

在这里插入图片描述

读者模块

这个模块只有图书管理员有这个权限,故原理一样,代码如下:

// 判断函数,ReaderManage.c头上实现
static bool canOp()
{
	extern User* get_user;
	if (!user_isBookAdmin(get_user)) {
		printf("您没有此权限,只有图书管理员有这个权限~~\n");
		return false;
	}
	return true;
}

// 在每一个功能之前加上判断
if (canOp()) {
	return;
}

用户模块

只有系统管理员有这个权限,除修改用户密码外,但是代码还是一样:

// 判断, 因为就是usermange.c中,故不用extern获取了,所以也不用封装,在相应的功能前加上即可。
if (!user_isSysAdmin(get_user)) {
	printf("您没有权限,只有系统管理员有~~\n");
	return;
}

3、保存完善

这一步,就是去掉我们之前注释菜单前的清空页面处理,system("cls")

接下来就是保存到文件中

  • 首先创建退出模块,在每一个模块都定义,最后在main函数中调用退出函数。
// 封装退出模块,其中每一个内容
void management_quit(ManageMent* m)
{
	// 每个文件定义退出, 这里为了简单,直接传递文件名的方式
	userManage_quit(&m->userManage, "./data/user.txt");
	readerManage_quit(&m->readerManage, "./data/reader.txt");
	bookManage_quit(&m->bookManage, "./data/book/books.txt");
	circulateManage_quit(&m->circulateManage, "./data/circulate.txt");

	exit(0);
}
  • 实现
// usermanage.c
void userManage_quit(UserManage* umage)
{
	//保存文件
	FILE* fp = fopen(umage->filename, "w");
	if (!fp)
	{
		perror("file open failed");
		return;
	}
	fputs("账户名\t密码\t权限类型\n", fp);
	Node* curNode = umage->userlist.front->next;
	while (curNode)
	{
		user_save(curNode->data, fp);
		curNode = curNode->next;
	}
	list_transfrom(&umage->userlist, user_print);
	fclose(fp);
}

// readermanage.c
void readerManage_quit(ReaderManager* readerm, const char* filename)
{
	// 打开文件
	FILE* fp = fopen(filename, "w");
	if (!fp) {
		printf("funcation is %s, 打开文件失败\n", __FUNCTION__);
		return;
	}

	// 定义缓冲区
	char buff[BUFSIZ] = { 0 };

	// 输出表头
	fputs("readerNo\treaderName\tdept\ttel\tcnt\tcnt\n", fp);

	// 输出内容
	Node* cur = readerm->bookManageList.front->next;

	while (cur) {
		memset(buff, 0, sizeof(buff));

		Reader* tmp = cur->data;

		// 存储
		sprintf(buff, "%d\t%s\t%s\t%s\t%d\t%d\n", tmp->reader_number, tmp->reader_name, tmp->reader_company, tmp->reader_community, tmp->reader_collection_count, tmp->reader_borrow_count);
		fputs(buff, fp);

		cur = cur->next;
	}

	fclose(fp);
}

// circulatemanage.c
void circulateManage_quit(CirculateManage* cirm, const char* filename)
{
	// 打开文件
	FILE* fp = fopen(filename, "w");
	if (!fp) {
		printf("funcation is %s, 打开文件失败\n", __FUNCTION__);
		return;
	}

	// 定义缓冲区
	char buff[BUFSIZ] = { 0 };

	// 输出表头
	fputs("读者号\t书号\t结束日期\t还书日期\t备注\n", fp);

	// 输出内容
	Node* cur = cirm->cirManage.front->next;

	while (cur) {
		memset(buff, 0, sizeof(buff));

		Circulate* tmp = cur->data;

		// 存储
		sprintf(buff, "%llu\t%d\t%s\t%s\t%s\n", tmp->reader_num, tmp->book_num, tmp->borrow_date, tmp->return_data, tmp->comment);
		fputs(buff, fp);

		cur = cur->next;
	}

	fclose(fp);
}

// bookmanage.c
void bookManage_quit(BookManage* bookm, const char* filename)
{
	// 打开文件
	FILE* fp = fopen(filename, "w");
	if (!fp) {
		printf("funcation is %s, 打开文件失败\n", __FUNCTION__);
		return;
	}

	// 定义缓冲区
	char buff[BUFSIZ] = { 0 };

	// 输出表头
	fputs("记录号\t书号\t书名\t作者\t出版社\t藏书量\t借出数\t指针1\t指针2\t指针3", fp);

	// 输出内容
	for (int i = 0; i < bookm->bookManages.size; i++) {
		memset(buff, 0, sizeof(buff));

		Book* book = bookm->bookManages.data[i];

		sprintf(buff, "%d\t%d\t%s\t%s\t%s\t%d\t%d\t%d\t%d\t%d\n", book->reacod_num, book->book_num, book->book_name, book->book_author, book->book_publish, book->book_count, book->book_borrow_cnt, book->p1, book->p2, book->p3);

		fputs(buff, fp);
	}
	fclose(fp);
}
  • 简单测试,登录1990817账号,添加用户,结果如图:

在这里插入图片描述

4、进阶

这一部分主要是在第三部分图书搜索的时候,为不同的搜索方式分别建立索引(这里用顺序表),这一部分后面实现 🤠🤠🤠🤠🤠🤠🤠🤠🤠🤠🤠🤠🤠🤠,大家也可以思考一下如何实现,要求如下:

  • 在输入图书信息时建立图书主文件,在图书主文件中记录号从1开始,根据设计要求,在建立图书文件的同时,需要建立一个主关键字(书号)索引表。索引表按书号升序排列(用插入排序法),索引表可以先在内存中用一推数组实现,最后再将相应内容一并写入(外存)文件。

  • 根据设计要求,图书文件除了主关键字(书号)索引表外,还需要建立书名、作者、出版社三个次关键字索引表。次关键字索引表可采用头插法建立,具体做法是:根据一个主文件的记录,将要建立索引的次关键字与对应的次关键字索引表中的次关键字(如书名、作者、出版社)进行比较,若有相等的,就将主文件中的相应指针修改为索引表中的当前链头指针,并修改相应索引表中的链头指针为当前主文件的记录指针(即记录号),同时将长度加1;若没有相等的,就将主文件中的相应指针置为0,并在相应次关键字索引表中增加与该次关键字相关的一条记录,该记录的链头指针置为当前主文件的记录号,而将长度置为1。

代码网盘

通过网盘分享的文件:C-图书管理系统.zip
链接: https://pan.baidu.com/s/1hv6y4xiVdOLkj8GPr0OFIQ?pwd=nvpv 提取码: nvpv

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2180840.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

发布-订阅模式演示示例

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>发布-订阅模式示例</title><styl…

LC记录一:寻找旋转数组最小值、判断旋转数组是否存在给定元素

文章目录 33.搜索旋转排序数组81.搜索旋转排序数组||153.寻找旋转排序数组中的最小值154.寻找旋转排序数组中的最小值||参考链接 33.搜索旋转排序数组 https://leetcode.cn/problems/search-in-rotated-sorted-array/description/ 下面这张图片是LC154题官方题解提供的一个图…

重磅!25年3月起,PMP®考试将启用新教材!

近期&#xff0c;PMI对各科目教材进行了调整。9月27日宣布了2025年3月将会更新ACP的考试内容&#xff0c;新版考试仍将围绕敏捷思维和产品交付&#xff0c;但考试内容大纲(ECO)将整合为四大领域&#xff08;思维、领导力、产品、交付&#xff09;&#xff0c;融合现代新的项目类…

DAY18||530.二叉搜索树的最小绝对值差 |501.二叉搜索树中的众数| 236.二叉树的最近公共祖先

530.二叉搜索树的最小绝对值差 题目&#xff1a;530. 二叉搜索树的最小绝对差 - 力扣&#xff08;LeetCode&#xff09; 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 …

MongoDB 快速入门+单机部署(附带脚本)

目录 介绍 体系结构 数据模型 BSON BSON 数据类型 特点 高性能 高可用 高扩展 丰富的查询支持 其他特点 部署 单机部署 普通安装 脚本安装 Docker Compose 安装 卸载 停止 MongoDB 删除包 删除数据目录 参考&#xff1a; https://docs.mongoing.com/ 介绍…

Ping到底干了啥?ICMP 协议详解

什么是 ICMP&#xff1f; ICMP&#xff08;Internet Control Message Protocol&#xff0c;互联网控制消息协议&#xff09;是一种网络层协议&#xff0c;主要用于在网络设备之间传递控制信息和错误消息。它是 IP 协议族的一部分&#xff0c;通常与 IP 协议一起使用。ICMP 的主…

cheese自动化平台开发环境搭建【图文版】

目的 cheese自动化平台是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。可以采用Vscode、IDEA编写&#xff0c;支持Java、Python、nodejs、GO、Rust、Lua。官方提供了视频版教程&#xff0c;对于…

leetcode每日一题day20(24.9.30)——座位预约管理系统

思路&#xff1a;由于一直是出最小的编号&#xff0c;并且除此之外只有添加元素的操作&#xff0c;如果使用数组存储&#xff0c;并记录&#xff0c;这样出最小编号时间是O(n)复杂度,释放一个座位则是O(1)在操作出线机会均等的情况下&#xff0c;平均是O(n/2), 但对于这种重复 …

CANoe_trace介绍以及如何使用C#仿制trace方案介绍

C#UI界面仿制trace界面介绍--初次制作&#xff0c;后续待完善 在汽车电子开发中&#xff0c;DBC&#xff08;Database Container&#xff09;文件对于定义和描述CAN&#xff08;Controller Area Network&#xff09;通信协议至关重要。随着项目的迭代和功能的扩展&#xff0c;手…

Elasticsearch 开放推理 API 增加了对 Google AI Studio 的支持

作者&#xff1a;来自 Elastic Jeff Vestal 我们很高兴地宣布 Elasticsearch 的开放推理 API 支持 Gemini 开发者 API。使用 Google AI Studio 时&#xff0c;开发者现在可以与 Elasticsearch 索引中的数据进行聊天、运行实验并使用 Google Cloud 的模型&#xff08;例如 Gemin…

0基础学前端 day7

大家好&#xff0c;欢迎来到无限大的频道 今天继续带领大家来了解前端的知识&#xff0c;深入了解互联网和浏览器背后的技术。 历史背景 互联网的起源可以追溯到20世纪60年代的ARPANET项目&#xff0c;作为研究机构之间的通信网络。最初的网页浏览器由Tim Berners-Lee于1990…

【工程测试技术】第3章 测试装置的基本特性,静态特性和动态特性,一阶二阶系统的特性,负载效应,抗干扰性

目录 3.1 概述 1测量装置的静态特性 2.标准和标准传递 3.测量装置的动态特性 4.测量装置的负载特性 5.测量装置的抗干扰性 1.线性度 2.灵敏度 3.回程误差 4.分辨力 5.零点漂移和灵敏度漂移 3.3.1 动态特性的数学描述 1.传递函数 2.频率响应函数 3.脉冲响应函数 …

jmeter进行性能测试实践

设置场景接口 一、通过抓取一个场景的接口&#xff08;抓包&#xff09; 自己抓取需要的接口&#xff0c;进行依赖 流程&#xff1a;1.在网页上F12抓取登录页面和登出页面的URL。2.在jemeter设置线程组&#xff0c;添加http请求输入URL等。3.查看结果数 二、通过boday录制 …

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-29

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-29 在这一期中&#xff0c;我们对大语言模型在软件开发中的跨学科应用的几个工作做简要的介绍。相关内容涵盖软件测试时的问题报告&#xff0c;问题分类&#xff0c;测试生成&#xff0c;和软件测试中的AI应用: …

【宝藏篇】加密软件有哪些?10款好用的加密软件推荐!

小明&#xff1a;嘿&#xff0c;小华&#xff0c;你知道有哪些好用的加密软件吗&#xff1f;我最近需要保护一些敏感数据。 小华&#xff1a;当然&#xff0c;小明&#xff01;现在市场上有很多优秀的加密软件&#xff0c;可以帮助你保护数据安全。我正好有10款宝藏级的加密软件…

【RocketMQ】RocketMQ应用难点

&#x1f3af; 导读&#xff1a;本文探讨了RocketMQ中消息重复消费的问题及其解决方案&#xff0c;尤其是在CLUSTERING模式下的扩容影响。文章分析了重复消费的原因&#xff0c;如广播模式、负载均衡模式下的多consumerGroup消费、消费者组内的动态变化及网络延迟等&#xff0c…

基于单片机的催眠电路控制系统

** 文章目录 前言一 概要功能设计设计思路 软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主…

动手学深度学习(李沐)PyTorch 第 3 章 线性神经网络

3.1 线性回归 线性回归是对n维输入的加权&#xff0c;外加偏差 线性回归可以看作是单层神经网络 回归问题中最常用的损失函数是平方误差函数。 平方误差可以定义为以下公式&#xff1a; 常数1/2不会带来本质的差别&#xff0c;但这样在形式上稍微简单一些 &#xff08;因为当…

【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧

文章目录 C模板进阶编程前言第一章: 非类型模板参数1.1 什么是非类型模板参数&#xff1f;1.1.1 非类型模板参数的定义 1.2 非类型模板参数的注意事项1.3 非类型模板参数的使用场景示例&#xff1a;静态数组的实现 第二章: 模板的特化2.1 什么是模板特化&#xff1f;2.1.1 模板…

YOLO11关键改进与网络结构图

目录 前言&#xff1a;一、YOLO11的优势二、YOLO11网络结构图三、C3k2作用分析四、总结 前言&#xff1a; 对于一个科研人来说&#xff0c;发表论文水平的高低和你所掌握的信息差有着极大的关系&#xff0c;所以趁着YOLO11刚刚发布&#xff0c;趁热了解&#xff0c;先人一步对…