一、前言
最近根据学校需求做了一个小项目,人事管理系统,用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;
}