人事管理系统(C语言)

一、前言

最近根据学校需求做了一个小项目,人事管理系统,用C语言进行实现,C语言学了有很长一段时间,一些细节的知识点也遗忘许多。当初学习C语言课程设计做的通讯录项目,有很多知识点感觉没有用到,做出来的功能也比较单一,比较遗憾。所以此次项目本着复习一遍C语言的目的,并且希望将当初学习的知识尽数用上。


二、设计内容

2.1 设计概述

​ 使用C语言设计一个人事管理系统,功能包括增加、删除、修改、查找、展示信息、排序信息、清空信息、保存信息、展示小组信息 等功能。

​ 运行程序时,系统会加载存储员工信息的文件(.txt),如果不存在,会给于提示。然后进入系统进行增删改查等操作,对员工信息的处理等操作必须在保存后,才能录入文件,如果中途意外退出并不会保存,点击退出系统功能会自动保存。

主界面展示如下:

在这里插入图片描述

2.2 设计需求

员工信息存储包含以下信息:

工号、姓名、年龄、性别、电话、薪资、职务

存储信息,我们默认工号是不可重复的,姓名是可以重复的

功能

  • 添加:先一次确定要增加几个信息,然后再进行每个员工的具体信息录入
  • 删除:按工号删除,按姓名删除,姓名删除取的是信息表的第一个进行姓名进行删除
  • 修改:提供工号修改和姓名修改,与删除类似
  • 查找:按照姓名查找,按工号查找;按姓名查找,会显示信息表所有重合姓名的情况(工号查找二分查找算法实现)
  • 展示信息:格式化展示信息表所有员工信息
  • 排序:按工号、按职位、按薪资排序员工信息
  • 清空:清除信息表所有信息
  • 保存:对信息表操作完后,需要保存,才能将信息永久录入文件
  • 展示小组信息:展示创作者信息

用到的知识点

结构体、动态顺序表相关操作、文件操作、枚举、qsort排序,二分查找

因为二分查找特点,这里每个功能实现用到二分查找的都会默认进行工号排序

2.3 系统概述

对整个设计需求分析后,具体模块实现如下:

在这里插入图片描述


三、详细设计

这里叙述主要功能实现,具体细节可以看源代码章节

每个模块包括实现效果、关键点、思路图、代码、部分运行截图

3.1 前提准备

数据存储

#define ID_MAXSIZE 10
#define NAME_MAXSIZE 20
#define TEL_MAXSIZE 15
#define OFFICIALNAME_MAXSIZE 10
#define SEX_MAXSIZE 5

//职员信息
typedef struct Employee {
	char name[NAME_MAXSIZE];  //姓名
	char ID[ID_MAXSIZE];      //工号
	int age;		          //年龄
	char sex[SEX_MAXSIZE];	  //性别
	char tel[TEL_MAXSIZE];	  //电话
	int salary;				  //薪资
	char officialName[OFFICIALNAME_MAXSIZE];  //职务
}Employee;

//动态信息表
typedef struct PIM {
	Employee* emp;  //存放职员数组
	int size;       //长度
	int capacity;   //容量
}PIM;

函数声明

//声明函数
//初始信息表
void Init_PIM(PIM * pm);
//添加信息
void Add_PIM(PIM * pm);
//打印信息
void Show_PIM(const PIM * pm);
//删除信息
void Del_PIM(PIM * pm);
//查找指定人信息
void Search_PIM(const PIM* pm);
//修改指定人信息
void Modify_PIM(PIM * pm);
//排序信息表内容
void Sort_PIM(PIM * pm);
//清空信息表
void Clear_PIM(PIM * pm);
//释放内存
void Destroy_PIM(PIM* pm);
//保存文件
void Save_PIM(PIM * pm);
//加载文件信息到信息表
void Load_PIM(PIM * pm);

//封装函数(只允许编写文件调用,外部不能调用)
//增容
static void CheckCapacity(PIM* pm);
//姓名查找
static int FinidByName(const PIM* pm,char* str);
//ID查找
static int FindByID(const PIM* pm,char* ID);
//按ID排序
static int compare_emp_ID(void* p1, void* p2);
//按姓名排序
static int compare_emp_name(void* p1, void* p2);
//按薪水排序
static int compare_emp_salary_asc(void* p1, void* p2);
static int compare_emp_salary_desc(void* p1, void* p2);

3.2 员工信息添加

实现效果

输入添加信息个数,然后依次添加每个员工的具体信息

关键点

1.每次添加信息,需要利用CheckCapacity函数检查信息表空间是否足够,不够则扩充

2.如何将输入的信息正确保存到信息表

思路图:

在这里插入图片描述
代码展示

//添加信息
void Add_PIM(PIM* pm)
{
	printf("请输入要添加的人事信息个数:>\n");
	int num = 0;
	scanf("%d", &num);
	int count = 1;
	system("cls");		//清屏
	while (num)
	{
		//检查空间是否 足够
		CheckCapacity(pm);

		printf("请添加第%d员工相关信息\n", count);
		count++;
		printf("请输入员工姓名:>\n");
		scanf("%s", pm->emp[pm->size].name);
		printf("请输入员工工号:>\n");
		scanf("%s", pm->emp[pm->size].ID);
		printf("请输入员工年龄:>\n");
		scanf("%d", &pm->emp[pm->size].age);
		printf("请输入员工性别:>\n");
		scanf("%s", pm->emp[pm->size].sex);
		printf("请输入员工电话:>\n");
		scanf("%s", pm->emp[pm->size].tel);
		printf("请输入员工薪水:>\n");
		scanf("%d", &pm->emp[pm->size].salary);
		printf("请输入员工职务:>\n");
		scanf("%s", pm->emp[pm->size].officialName);

		pm->size++;
		num--;
		//实现清屏效果,避免冗余
		system("cls");
	}
}

//增容 	函数正式编写需在调用函数前,这里便于阅读放后
static void CheckCapacity(PIM* pm)
{
	if (pm->size == pm->capacity)
	{
		//realloc扩容
		PIM* tmp = realloc(pm->emp,sizeof(Employee) * (pm->capacity+2));//一次扩两个空间
		if (tmp == NULL)
		{
			printf("扩容失败\n");
			exit(0);
		}
		pm->emp = tmp;
		pm->capacity += 2;
	}
}

效果如下:

在这里插入图片描述

在这里插入图片描述


3.3 员工信息删除

实现效果

分为工号删除和姓名删除两种方式。

选择工号删除时系统会自动对信息表进行工号排序;

选择姓名删除,系统默认删除信息第一个姓名相关信息

关键点

判断信息表是否为空

通过FindByID和FindByName函数实现查找返回索引,然后再进行删除操作

思路图:

在这里插入图片描述

(流程图有些瑕疵)

代码展示

//删除信息
void Del_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的删除方式:> 1.工号删除(自动排序)    2.姓名删除\n");
	scanf("%d", &choice);
	//获取索引 操作
	int res;
	if (choice == 1)
	{
		char ID[ID_MAXSIZE];
		printf("请输入要删除的员工工号\n");
		scanf("%s", ID);
		res = FindByID(pm, ID);		//返回索引
	}
	else if (choice == 2)
	{
		char name[NAME_MAXSIZE];
		printf("请输入要删除的员工姓名\n");
		scanf("%s", name);
		res = FinidByName(pm, name);  //返回索引
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	//删除操作
	if (res == -1)
	{
		printf("要删除的员工不存在\n");
		return;
	}
	else
	{
		//展示删除员工信息
		printf("姓名:%s\t工号:%s\t职务:%s\t\n", pm->emp[res].name, pm->emp[res].ID, pm->emp[res].officialName);
		int j;
		for (j = res; j < pm->size - 1; j++)
			pm->emp[j] = pm->emp[j + 1];
		pm->size--;
		printf("删除成功\n");
	}
}

FindByID、FindByName函数编写见 3.9其他设计

运行截图:

在这里插入图片描述


3.4 员工信息修改

实现效果

分为工号修改和姓名修改两种方式,与信息删除功能原理类似,如果信息存在,则进行修改。如果不存在,系统给予提示

思路图:

在这里插入图片描述

代码展示

//修改指定人信息
void Modify_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的修改方式:> 1.工号修改(工号自动排序)    2.姓名修改\n");
	scanf("%d", &choice);
	//获取索引 操作
	int res;
	if (choice == 1)
	{
		char ID[ID_MAXSIZE];
		printf("请输入要修改信息的员工工号\n");
		scanf("%s", ID);
		res = FindByID(pm, ID);		//返回索引
	}
	else if (choice == 2)
	{
		char name[NAME_MAXSIZE];
		printf("请输入要修改信息的员工姓名\n");
		scanf("%s", name);
		res = FinidByName(pm, name);  //返回索引
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	//展示需修改的员工信息
	printf("姓名:%s\t工号:%s\t职务:%s\t\n", pm->emp[res].name, pm->emp[res].ID, pm->emp[res].officialName);
	//修改操作
	if (res == -1)
	{
		printf("要修改的员工不存在\n");
		return;
	}
	else
	{
		printf("请输入员工姓名:>\n");
		scanf("%s", pm->emp[res].name);
		printf("请输入员工工号:>\n");
		scanf("%s", pm->emp[res].ID);
		printf("请输入员工年龄:>\n");
		scanf("%d", &pm->emp[res].age);
		printf("请输入员工性别:>\n");
		scanf("%s", pm->emp[res].sex);
		printf("请输入员工电话:>\n");
		scanf("%s", pm->emp[res].tel);
		printf("请输入员工薪水:>\n");
		scanf("%d", &pm->emp[res].salary);
		printf("请输入员工职务:>\n");
		scanf("%s", pm->emp[res].officialName);
		printf("修改成功\n");
	}
}

FindByID、FindByName函数编写见 3.9其他设计


3.5 员工信息查找

实现效果

分为工号查询和姓名查询两种方式。

选择工号查询,系统会自动对信息表进行排序,并且查询工号对应的信息结果;

选择姓名查询,系统会查询信息表所有对应姓名信息(包括重名)

关键点

FindByID函数和FindByName函数编写

思路图:

在这里插入图片描述

代码展示

//查找指定的人的信息
void Search_PIM(const PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的查询方式:> 1.工号查询(自动排序)    2.姓名查询\n");
	scanf("%d", &choice);
	//获取索引 操作
	int res;
	if (choice == 1)
	{
		char ID[ID_MAXSIZE];
		printf("请输入要查询的员工工号\n");
		scanf("%s", ID);
		res = FindByID(pm, ID);		//返回索引
		if (res == -1)
		{
			printf("工号不存在\n");
			return;
		}
		printf("\n");
		printf("查询结果如下: \n");
		printf("%-20s\t%-10s\t%-4s\t%-4s\t%-15s\t%-8s\t%-8s\n", "姓名", "工号", "年龄", "性别", "电话", "薪资", "职务");
		printf("%-20s\t%-10s\t%-5d\t%-4s\t%-15s\t%-8d\t%-8s\n",
			pm->emp[res].name,
			pm->emp[res].ID,
			pm->emp[res].age,
			pm->emp[res].sex,
			pm->emp[res].tel,
			pm->emp[res].salary,
			pm->emp[res].officialName);
	}
	else if (choice == 2)
	{
		char name[NAME_MAXSIZE];
		printf("请输入要查询的员工姓名\n");
		scanf("%s", name);
		int i = 0;
		int res = FinidByName(pm, name);   //先查询姓名是否存在
		if (res == -1)
		{
			printf("查无此人\n");
			return;
		}
		printf("\n");
		printf("查询结果如下: \n");
		//具体查询姓名重复的情况
		printf("%-20s\t%-10s\t%-5s\t%-4s\t%-15s\t%-8s\t%-8s\n", "姓名", "工号", "年龄", "性别", "电话", "薪资", "职务");
		for (i = res; i < pm->size; i++)
		{
			if (0 == strcmp(pm->emp[i].name, name))
			{
				printf("%-20s\t%-10s\t%-5d\t%-4s\t%-15s\t%-8d\t%-8s\n",
					pm->emp[i].name,
					pm->emp[i].ID,
					pm->emp[i].age,
					pm->emp[i].sex,
					pm->emp[i].tel,
					pm->emp[i].salary,
					pm->emp[i].officialName);
			}
		}
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
}

FindByID、FindByName函数编写见 3.9其他设计

运行截图:

在这里插入图片描述


3.6 员工信息展示

实现效果

将信息表信息,按照一定的格式进行打印,具有可观看性

关键点

占位符和制表符的使用

代码展示

//打印信息
void Show_PIM(const PIM* pm)
{
	if (pm->size == 0)
		printf("人事管理系统暂无信息,请进行添加 > \n");
	else
	{
		//标题
		int i = 0;
		printf("查询成功\n");
		printf("%-20s\t%-10s\t%-5s\t%-4s\t%-15s\t%-8s\t%-8s\n", "姓名", "工号", "年龄", "性别", "电话", "薪资", "职务");
		//数据
		//格式化输出
		for (i = 0; i < pm->size; i++)
		{
			printf("%-20s\t%-10s\t%-5d\t%-4s\t%-15s\t%-8d\t%-8s\n",
				pm->emp[i].name,
				pm->emp[i].ID,
				pm->emp[i].age,
				pm->emp[i].sex,
				pm->emp[i].tel,
				pm->emp[i].salary,
				pm->emp[i].officialName);
		}
	}
}

运行截图:

在这里插入图片描述


3.7 员工信息排序

实现效果

分为三种排序,工号排序,姓名排序,薪资排序正序,薪资排序倒序

关键点

1.利用qsort函数实现排序,编写compare函数传入函数指针,compare函数强制类型转换

2.如何实现工号字符串间的排序

思路图:

在这里插入图片描述
代码展示

static int compare_emp_ID(void* p1, void* p2)
{
	return atoi(((Employee*)p1)->ID) - atoi(((Employee*)p2)->ID);
	//先将p1强制类型转换为 Employee类型指针 ,指向ID后,再由字符串转换为整型(atoi函数),比较大小
}
static int compare_emp_name(void* p1, void* p2)
{
	return strcmp(((Employee*)p1)->name, ((Employee*)p2)->name);
	//先强制类型转,再由strcmp函数比较结构体 字符串
}
static int compare_emp_salary_asc(void* p1, void* p2)
{
	return ((Employee*)p1)->salary - ((Employee*)p2)->salary;
	//先强制类类型转换,再通过相减比较大小返回正负
}
static int compare_emp_salary_desc(void* p1, void* p2)
{
	return ((Employee*)p2)->salary - ((Employee*)p1)->salary;
	//先强制类类型转换,再通过相减比较大小返回正负
}
//排序信息表内容
void Sort_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的排序方式:> 1.工号排序    2.姓名排序(首字母顺序)    3.薪资排序(正序)    4.薪资排序(倒序)\n");
	scanf("%d", &choice);

	if (choice == 1)
	{
		//存放员工信息结构体数组   数组长度  结构体所占空间   compare函数
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_ID);
	}
	else if (choice == 2)
	{
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_name);
	}
	else if (choice == 3)
	{
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_salary_asc);
	}
	else if (choice == 4)
	{
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_salary_desc);
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	printf("排序成功\n");
}
#include<stdlib.h>

void qsort( void *base, size_t num, size_t width, int (__cdecl  compare )(const void *elem1,  const void *elem2 ) );
//参数:排序数组  数组长度  数组元素大小  compare函数
//重写compare函数,比较两个元素大小,返回int类型

int atoi(const char* string);
//将字符串转换为整形
//"0021" -> 21

运行截图:

工号排序

在这里插入图片描述

姓名排序

在这里插入图片描述

薪资排序

在这里插入图片描述


3.8 员工信息载入和保存

实现效果

管理系统对信息表的处理结果可以以文件的形式永久保存,并在下次启动系统时可以将文件信息读入,进行增删改查

关键点

对文件输入时保持信息的格式化

文件信息读取时保证变量能够成功读取文件数据

代码展示

//加载文件信息到信息表
void Load_PIM(PIM* pm)
{
	FILE* pf = fopen("emp.txt", "r");
	if (pf == NULL)
	{
		printf("Load_PIM : %s\n", strerror(errno));
		//errno 获取错误码,将出错具体原因通过strerror函数呈现
		//读入文件时,如果不存在文件,提示作用
		return;
	}
	int num = 0;
	fscanf(pf, "%d\n", &num);
	int i;
	for (i = 0; i < num; i++)
	{
		CheckCapacity(pm);
		fscanf(pf, "%s %s %d %s %s %d %s ",
			pm->emp[i].name,
			pm->emp[i].ID,
			&pm->emp[i].age,
			pm->emp[i].sex,
			pm->emp[i].tel,
			&pm->emp[i].salary,
			pm->emp[i].officialName);
	}
	pm->size = num;
	printf("文件读入成功\n");

	fclose(pf);
	pf = NULL;
}

//保存文件
void Save_PIM(PIM* pm)
{
	FILE* pf = fopen("emp.txt", "w");
	if (pf == NULL)
	{
		printf("There was an error: %s", strerror(errno));
		//errno 获取错误码,将出错具体原因通过strerror函数呈现
		return;
	}
	fprintf(pf, "%d\n", pm->size); //将存储信息个数保存
	int i = 0;
	for (i = 0; i < pm->size; i++)
	{
		//将信息格式化保存
		fprintf(pf,"%-20s\t%-10s\t%-5d\t%-5s\t%-15s\t%-8d\t%-8s\n",
				pm->emp[i].name,
				pm->emp[i].ID,
				pm->emp[i].age,
				pm->emp[i].sex,
				pm->emp[i].tel,
				pm->emp[i].salary,
				pm->emp[i].officialName);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}


3.9 其他设计

FindByID、FindByName函数
static int FindByID(PIM* pm, char* ID)
{
	//首先要将信息表排序(按照工号排序)
	qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_ID);

	int left = 0;
	int right = pm->size - 1;
	//等号 : 假如只有一个信息的时候
	while (left <= right)
	{
		int mid = left + ((right - left) >> 1); // 类似left + (right - left)/2
		//目标值小于中间值,左区间搜
		if (atoi(pm->emp[mid].ID) > atoi(ID))
			right = mid  - 1;
		else if (atoi(pm->emp[mid].ID) < atoi(ID))
			left = mid + 1;
		else
			return mid;//返回索引
	}
	return -1;
}

static int FinidByName(const PIM* pm, char* name)
{
	int i = 0;
	for (int i = 0; i < pm->size; i++) 
	{
		if (0 == strcmp(pm->emp[i].name, name))
			return i;
	}

	return -1;//找不到
}
枚举

利用枚举特性,将每个模块枚举,在switch选择语句中使用

enum option
{
EXIT,  //0
ADD,   //1
DEL,   //2
SHOW,
SEARCH,
MOD,
SORT,
CLEAR,
SAVE,
SHOW_TEAM,
};

清空信息表

用户可以选择一键清除信息表,不用重复进行删除操作,清空信息表需再次输入’Y’

//清除信息表
void Clear_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	char choice;
	//再次确认是否选择清除所有数据
	getchar(); //将\n吸收
	printf("请再次确认是否选择清除所有数据:>(Y or N)\n");
	scanf("%c", &choice);
	if (choice == 'Y')
	{
		free(pm->emp);
		pm->capacity = 4;
		pm->size = 0;
		printf("清除成功\n");
	}
	else if (choice == 'N')
		printf("退出清除\n");
	else 
	{
		printf("输入错误!\n");
	}
}

四、疑问总结收获

此处叙述本次项目出现的一些错误,包括细节知识点的遗忘,问题的解决和注意,有些问题还没有想清楚,先记录下来

  • 数组的连续输入
for (i = 0; i < 6; i++)
{
	scanf("%d", &arr[i]);
}
//输入1 2 3 4 5 6
//1,2,3,4,5,6  err

中间的数字间只能用空格隔开,不能用逗号等其他字符隔开

  • getchar使用时机,接收\n

在这里插入图片描述

当我将性别改成char* 类型,前后getchar可以去掉

  • 字符存储(写程序犯了最基本错误)

在这里插入图片描述

  • exit和return区别

在这里插入图片描述

exit结束整个进程,return结束某个函数

  • qsort函数和atoi函数使用

对字符串排序、compare函数强制类型转换的应用,结构体类型强制转换注意

  • 文件读入读出格式问题

在这里插入图片描述

在这里插入图片描述

文件读取,当采用 fscanf(pf,"%-20s\t%-10S……")时,即格式与fprintf一致,读入时会产生乱码现象

总:文件读入时,数组并不会将\t ,\n等符号读入数组,默认的跳过;在fputs和fgets函数可能不一样,它会将符号都读入

  • FindByID函数实现

本次项目的查找用到了二分查找,也想过用哈希查找,这样就不会有每次调用FindByID函数时需要自动排序的问题,也想过哈希+二分,没仔细去深究,在此记录想法


五、源代码

PIM.h

#define ID_MAXSIZE 10
#define NAME_MAXSIZE 20
#define TEL_MAXSIZE 15
#define OFFICIALNAME_MAXSIZE 10
#define SEX_MAXSIZE 5

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

enum option
{
	EXIT,  //0
	ADD,   //1
	DEL,   //2
	SHOW,
	SEARCH,
	MOD,
	SORT,
	CLEAR,
	SAVE,
	SHOW_TEAM,
};

//职员信息
typedef struct Employee {
	char name[NAME_MAXSIZE];  //姓名
	char ID[ID_MAXSIZE];      //工号
	int age;		          //年龄
	char sex[SEX_MAXSIZE];	  //性别
	char tel[TEL_MAXSIZE];	  //电话
	int salary;				  //薪资
	char officialName[OFFICIALNAME_MAXSIZE];  //职务
}Employee;

//动态信息表
typedef struct PIM {
	Employee* emp;  //存放职员数组
	int size;       //长度
	int capacity;   //容量
}PIM;

//声明函数
//初始信息表
void Init_PIM(PIM * pm);

//添加信息
void Add_PIM(PIM * pm);

//打印信息
void Show_PIM(const PIM * pm);
//删除信息
void Del_PIM(PIM * pm);
//查找指定人信息
void Search_PIM(const PIM* pm);
//修改指定人信息
void Modify_PIM(PIM * pm);
//排序信息表内容
void Sort_PIM(PIM * pm);
//清空信息表
void Clear_PIM(PIM * pm);
//释放内存
void Destroy_PIM(PIM* pm);
//保存文件
void Save_PIM(PIM * pm);
//加载文件信息到信息表
void Load_PIM(PIM * pm);

//封装函数
//增容
static void CheckCapacity(PIM* pm);
//姓名查找
static int FinidByName(const PIM* pm,char* str);
//ID查找
static int FindByID(const PIM* pm,char* ID);
//按ID排序
static int compare_emp_ID(void* p1, void* p2);
//按姓名排序
static int compare_emp_name(void* p1, void* p2);
//按薪水排序
static int compare_emp_salary_asc(void* p1, void* p2);
static int compare_emp_salary_desc(void* p1, void* p2);

PIM.c

#define _CRT_SECURE_NO_WARNINGS

#include"PIM.h"

//初始信息表
void Init_PIM(PIM* pm)
{
	pm->size = 0;
	pm->capacity = 4;
	pm->emp = (Employee*)malloc(4 * sizeof(Employee));
	if (pm->emp == NULL)
		return;
	Load_PIM(pm);
}


//增容
static void CheckCapacity(PIM* pm)
{
	if (pm->size == pm->capacity)
	{
		//realloc扩容
		PIM* tmp = realloc(pm->emp,sizeof(Employee) * (pm->capacity+2));//一次扩两个空间
		if (tmp == NULL)
		{
			printf("扩容失败\n");
			exit(0);
		}
		pm->emp = tmp;
		pm->capacity += 2;
	}
}


//加载文件信息到信息表
void Load_PIM(PIM* pm)
{
	FILE* pf = fopen("emp.txt", "r");
	if (pf == NULL)
	{
		printf("Load_PIM : %s\n", strerror(errno));
		//errno 获取错误码,将出错具体原因通过strerror函数呈现
		//读入文件时,如果不存在文件,提示作用
		return;
	}
	int num = 0;
	fscanf(pf, "%d\n", &num);
	int i;
	for (i = 0; i < num; i++)
	{
		CheckCapacity(pm);
		fscanf(pf, "%s %s %d %s %s %d %s ",
			pm->emp[i].name,
			pm->emp[i].ID,
			&pm->emp[i].age,
			pm->emp[i].sex,
			pm->emp[i].tel,
			&pm->emp[i].salary,
			pm->emp[i].officialName);
	}
	pm->size = num;
	printf("文件读入成功\n");

	fclose(pf);
	pf = NULL;
}

//添加信息
void Add_PIM(PIM* pm)
{
	printf("请输入要添加的人事信息个数:>\n");
	int num = 0;
	scanf("%d", &num);
	int count = 1;
	system("cls");		//清屏
	while (num)
	{
		//检查空间是否 足够
		CheckCapacity(pm);

		printf("请添加第%d员工相关信息\n", count);
		count++;
		printf("请输入员工姓名:>\n");
		scanf("%s", pm->emp[pm->size].name);
		printf("请输入员工工号:>\n");
		scanf("%s", pm->emp[pm->size].ID);
		printf("请输入员工年龄:>\n");
		scanf("%d", &pm->emp[pm->size].age);
		printf("请输入员工性别:>\n");
		scanf("%s", pm->emp[pm->size].sex);
		printf("请输入员工电话:>\n");
		scanf("%s", pm->emp[pm->size].tel);
		printf("请输入员工薪水:>\n");
		scanf("%d", &pm->emp[pm->size].salary);
		printf("请输入员工职务:>\n");
		scanf("%s", pm->emp[pm->size].officialName);

		pm->size++;
		num--;
		//实现清屏效果,避免冗余
		system("cls");
	}
}

//打印信息
void Show_PIM(const PIM* pm)
{
	if (pm->size == 0)
		printf("人事管理系统暂无信息,请进行添加 > \n");
	else
	{
		//标题
		int i = 0;
		printf("查询成功\n");
		printf("%-20s\t%-10s\t%-5s\t%-4s\t%-15s\t%-8s\t%-8s\n", "姓名", "工号", "年龄", "性别", "电话", "薪资", "职务");
		//数据
		//格式化输出
		for (i = 0; i < pm->size; i++)
		{
			printf("%-20s\t%-10s\t%-5d\t%-4s\t%-15s\t%-8d\t%-8s\n",
				pm->emp[i].name,
				pm->emp[i].ID,
				pm->emp[i].age,
				pm->emp[i].sex,
				pm->emp[i].tel,
				pm->emp[i].salary,
				pm->emp[i].officialName);
		}
	}
}
static int FinidByName(const PIM* pm, char* name)
{
	int i = 0;
	for (int i = 0; i < pm->size; i++) 
	{
		if (0 == strcmp(pm->emp[i].name, name))
			return i;
	}

	return -1;//找不到
}

static int compare_emp_ID(void* p1, void* p2)
{
	return atoi(((Employee*)p1)->ID) - atoi(((Employee*)p2)->ID);
	//先将p1强制类型转换为 Employee类型指针 ,指向ID后,再由字符串转换为整型(atoi函数),比较大小
}

static int FindByID(PIM* pm, char* ID)
{
	//首先要将信息表排序(按照工号排序)
	qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_ID);

	int left = 0;
	int right = pm->size - 1;
	//等号 : 假如只有一个信息的时候
	while (left <= right)
	{
		int mid = left + ((right - left) >> 1); // 类似left + (right - left)/2
		//目标值小于中间值,左区间搜
		if (atoi(pm->emp[mid].ID) > atoi(ID))
			right = mid  - 1;
		else if (atoi(pm->emp[mid].ID) < atoi(ID))
			left = mid + 1;
		else
			return mid;
	}
	return -1;
}


//删除信息
void Del_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的删除方式:> 1.工号删除(自动排序)    2.姓名删除\n");
	scanf("%d", &choice);
	//获取索引 操作
	int res;
	if (choice == 1)
	{
		char ID[ID_MAXSIZE];
		printf("请输入要删除的员工工号\n");
		scanf("%s", ID);
		res = FindByID(pm, ID);		//返回索引
	}
	else if (choice == 2)
	{
		char name[NAME_MAXSIZE];
		printf("请输入要删除的员工姓名\n");
		scanf("%s", name);
		res = FinidByName(pm, name);  //返回索引
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	//删除操作
	if (res == -1)
	{
		printf("要删除的员工不存在\n");
		return;
	}
	else
	{
		//展示删除员工信息
		printf("姓名:%s\t工号:%s\t职务:%s\t\n", pm->emp[res].name, pm->emp[res].ID, pm->emp[res].officialName);
		int j;
		for (j = res; j < pm->size - 1; j++)
			pm->emp[j] = pm->emp[j + 1];
		pm->size--;
		printf("删除成功\n");
	}
}

//查找指定的人的信息
void Search_PIM(const PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的查询方式:> 1.工号查询(自动排序)    2.姓名查询\n");
	scanf("%d", &choice);
	//获取索引 操作
	int res;
	if (choice == 1)
	{
		char ID[ID_MAXSIZE];
		printf("请输入要查询的员工工号\n");
		scanf("%s", ID);
		res = FindByID(pm, ID);		//返回索引
		if (res == -1)
		{
			printf("工号不存在\n");
			return;
		}
		printf("\n");
		printf("查询结果如下: \n");
		printf("%-20s\t%-10s\t%-4s\t%-4s\t%-15s\t%-8s\t%-8s\n", "姓名", "工号", "年龄", "性别", "电话", "薪资", "职务");
		printf("%-20s\t%-10s\t%-5d\t%-4s\t%-15s\t%-8d\t%-8s\n",
			pm->emp[res].name,
			pm->emp[res].ID,
			pm->emp[res].age,
			pm->emp[res].sex,
			pm->emp[res].tel,
			pm->emp[res].salary,
			pm->emp[res].officialName);
	}
	else if (choice == 2)
	{
		char name[NAME_MAXSIZE];
		printf("请输入要查询的员工姓名\n");
		scanf("%s", name);
		int i = 0;
		int res = FinidByName(pm, name);   //先查询姓名是否存在
		if (res == -1)
		{
			printf("查无此人\n");
			return;
		}
		printf("\n");
		printf("查询结果如下: \n");
		//具体查询姓名重复的情况
		printf("%-20s\t%-10s\t%-5s\t%-4s\t%-15s\t%-8s\t%-8s\n", "姓名", "工号", "年龄", "性别", "电话", "薪资", "职务");
		for (i = res; i < pm->size; i++)
		{
			if (0 == strcmp(pm->emp[i].name, name))
			{
				printf("%-20s\t%-10s\t%-5d\t%-4s\t%-15s\t%-8d\t%-8s\n",
					pm->emp[i].name,
					pm->emp[i].ID,
					pm->emp[i].age,
					pm->emp[i].sex,
					pm->emp[i].tel,
					pm->emp[i].salary,
					pm->emp[i].officialName);
			}
		}
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
}

//修改指定人信息
void Modify_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的修改方式:> 1.工号修改(工号自动排序)    2.姓名修改\n");
	scanf("%d", &choice);
	//获取索引 操作
	int res;
	if (choice == 1)
	{
		char ID[ID_MAXSIZE];
		printf("请输入要修改信息的员工工号\n");
		scanf("%s", ID);
		res = FindByID(pm, ID);		//返回索引
	}
	else if (choice == 2)
	{
		char name[NAME_MAXSIZE];
		printf("请输入要修改信息的员工姓名\n");
		scanf("%s", name);
		res = FinidByName(pm, name);  //返回索引
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	//展示需修改的员工信息
	printf("姓名:%s\t工号:%s\t职务:%s\t\n", pm->emp[res].name, pm->emp[res].ID, pm->emp[res].officialName);
	//修改操作
	if (res == -1)
	{
		printf("要修改的员工不存在\n");
		return;
	}
	else
	{
		printf("请输入员工姓名:>\n");
		scanf("%s", pm->emp[res].name);
		printf("请输入员工工号:>\n");
		scanf("%s", pm->emp[res].ID);
		printf("请输入员工年龄:>\n");
		scanf("%d", &pm->emp[res].age);
		printf("请输入员工性别:>\n");
		scanf("%s", pm->emp[res].sex);
		printf("请输入员工电话:>\n");
		scanf("%s", pm->emp[res].tel);
		printf("请输入员工薪水:>\n");
		scanf("%d", &pm->emp[res].salary);
		printf("请输入员工职务:>\n");
		scanf("%s", pm->emp[res].officialName);
		printf("修改成功\n");
	}
}


static int compare_emp_name(void* p1, void* p2)
{
	return strcmp(((Employee*)p1)->name, ((Employee*)p2)->name);
	//先强制类型转,再由strcmp函数比较结构体 字符串
}

static int compare_emp_salary_asc(void* p1, void* p2)
{
	return ((Employee*)p1)->salary - ((Employee*)p2)->salary;
	//先强制类类型转换,再通过相减比较大小返回正负
}
static int compare_emp_salary_desc(void* p1, void* p2)
{
	return ((Employee*)p2)->salary - ((Employee*)p1)->salary;
	//先强制类类型转换,再通过相减比较大小返回正负
}

//排序信息表内容
void Sort_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	int choice = 0;
	printf("请输入要选择的排序方式:> 1.工号排序    2.姓名排序(首字母顺序)    3.薪资排序(正序)    4.薪资排序(倒序)\n");
	scanf("%d", &choice);

	if (choice == 1)
	{
		//存放员工信息结构体数组   数组长度  结构体所占空间   compare函数
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_ID);
	}
	else if (choice == 2)
	{
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_name);
	}
	else if (choice == 3)
	{
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_salary_asc);
	}
	else if (choice == 4)
	{
		qsort(pm->emp, pm->size, sizeof(Employee), compare_emp_salary_desc);
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	printf("排序成功\n");
}

//清除信息表
void Clear_PIM(PIM* pm)
{
	if (pm->size == 0)
	{
		printf("人事管理系统暂无信息,请进行添加 > \n");
		return;
	}
	char choice;
	//再次确认是否选择清除所有数据
	getchar(); //将\n吸收
	printf("请再次确认是否选择清除所有数据:>(Y or N)\n");
	scanf("%c", &choice);
	if (choice == 'Y')
	{
		free(pm->emp);
		pm->capacity = 4;
		pm->size = 0;
		printf("清除成功\n");
	}
	else if (choice == 'N')
		printf("退出清除\n");
	else 
	{
		printf("输入错误!\n");
	}
}

//释放内存
void Destroy_PIM(PIM* pm)
{
	free(pm->emp);
	pm->capacity = 0;
	pm->size = 0;
	pm->emp = NULL;
}

//保存文件
void Save_PIM(PIM* pm)
{
	FILE* pf = fopen("emp.txt", "w");
	if (pf == NULL)
	{
		printf("There was an error: %s", strerror(errno));
		//errno 获取错误码,将出错具体原因通过strerror函数呈现
		return;
	}
	fprintf(pf, "%d\n", pm->size); //将存储信息个数保存
	int i = 0;
	for (i = 0; i < pm->size; i++)
	{
		//将信息格式化保存
		fprintf(pf,"%-20s\t%-10s\t%-5d\t%-5s\t%-15s\t%-8d\t%-8s\n",
				pm->emp[i].name,
				pm->emp[i].ID,
				pm->emp[i].age,
				pm->emp[i].sex,
				pm->emp[i].tel,
				pm->emp[i].salary,
				pm->emp[i].officialName);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}


Test.c

#include"PIM.h"
void Show_Team()
{
	printf("--------------------------\n");
	printf(">     创作者信息展示     <\n");
	printf(">      ***:*****      <\n");
	printf(">      ***:*****      <\n");
	printf(">                        <\n");
	printf(">  *****计算机科学与技术  <\n");
	printf(">  创作时间:2024.5.2   <\n");
	printf("--------------------------\n");
}

void menu()
{
	printf("--------------------------\n");
	printf(">      人事管理系统      <\n");
	printf(">     1.添加员工信息     <\n");
	printf(">     2.删除员工信息     <\n");
	printf(">     3.展示员工信息     <\n");
	printf(">     4.查找员工信息     <\n");
	printf(">     5.修改员工信息     <\n");
	printf(">     6.排序员工信息     <\n");
	printf(">     7.清空员工信息     <\n");
	printf(">     8.保存员工信息     <\n");
	printf(">     9.展示小组信息     <\n");
	printf(">     0.  退出系统       <\n");
	printf("--------------------------\n");
}


int main()
{
	int choice;
	PIM m;
	Init_PIM(&m);
	do 
	{
		menu();
		printf("请选择:>");
		scanf("%d", &choice);
		//通过枚举选择
		switch (choice)
		{
		case ADD:
			Add_PIM(&m);
			printf("添加成功\n");
			break;
		case DEL:
			Del_PIM(&m);
			break;
		case SHOW:
			system("cls");
			Show_PIM(&m);
			break;
		case SEARCH:
			Search_PIM(&m);
			break;
		case MOD:
			Modify_PIM(&m);
			break;
		case SORT:
			Sort_PIM(&m);
			break;
		case CLEAR:
			system("cls");
			Clear_PIM(&m);
			break;
		case SAVE:
			Save_PIM(&m);
			printf("保存成功\n");
			break;
		case SHOW_TEAM:
			system("cls");
			Show_Team();
			break;
		case EXIT:
			Save_PIM(&m);
			Destroy_PIM(&m);
			printf("退出系统\n");
			break;
		default:
			printf("无此选项\n");
			break;
		}
	} while (choice);
	return 0;
}


  • 31
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值