C语言实现员工管理系统

2024-06-17 13:44

本文主要是介绍C语言实现员工管理系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

员工管理系统

1. 题目要求

设计一个计算机程序,能够实现简单的员工管理功能。

  1. 每个员工的信息包括:编号、姓名、性别、出生年月、学历、职务、电话、住址等。
  2. 系统的功能包括:
    1. 文件操作:将数据输出到文件中以及从文件中加载数据
    2. 查询:按特定条件查找员工。
    3. 修改:按编号对某个员工的某项信息进行修改。
    4. 插入:加入新员工的信息。
    5. 删除:按编号删除已离职员工的信息。
    6. 排序:按特定条件对所有员工的信息进行排序。

2. 结构定义

2.1 员工结构定义

// 生日结构
typedef struct birthday
{int year;int month;int day;
}birthday;// 员工结构
typedef struct employee
{int id;char name[50];char gender[10];birthday birthday;char qualification[20];char job[30];char teleNum[15];char location[50];
}employee;

2.2 存储结构定义

typedef struct employee employee;
// 存储结构定义
typedef employee SLDataType;
typedef struct SeqList_dynamic
{SLDataType* SeqList;//指向可以修改大小的数据空间int size;//有效数据个数int capacity;//数据空间的总大小
}SL;//动态顺序表的实现
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//顺序表的扩容 
void SLCheckCapacity(SL* ps);
//顺序表的头部插⼊删除 / 尾部插⼊删除 
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//顺序表指定位置之前插⼊/删除数据 
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);

3. 项目文件

在本项目中,一共有五个文件

  1. SeqList.h:存放动态顺序表功能声明
  2. SeqList.c:存放动态顺序表功能定义
  3. employee.c:存放员工管理系统功能声明
  4. employee.h:存放员工管理系统功能定义
  5. test.c:测试文件

4. 项目功能

本项目主要实现下面的功能:

  1. 加载上次程序结束时保存到文件的员工信息
  2. 从控制台输入员工信息
  3. 查找指定信息的员工:仅包括id和姓名查找
  4. 修改员工的信息
  5. 插入一个新员工
  6. 删除指定员工
  7. 对员工信息按照指定信息排序:仅包括id、姓名和生日
  8. 打印所有员工信息
  9. 向文件中写入员工信息数据,便于下次读取

程序主菜单

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("*****************************\n");
}

程序功能对应函数

// 程序菜单
void menu();// 导入数据
void LoadEmployee(SL* employees);// 初始化——输入
void employeeInfoGet(SL* employees);// 查找员工
// 查找方式菜单
void menuForFind();
// 查找员工——返回下标
int findEmployee(SL* employees);
// 查找员工——显示对应员工信息
void findEmployee_print(SL* employees);// 修改菜单
void menuForModify();
// 修改员工信息
void modifyEmployee(SL* employees);// 插入员工信息
void insertEmployee(SL* employees);// 删除员工信息
void deleteEmployee(SL* employees);// 排序菜单
void menuForSort();
// 按照指定内容排序
void sortEmployees(SL* employees);
// 打印排序后的内容
void printSortedEmp(employee* tmp, int size);// 向文件中写入数据
void writeIntoFile(SL* employees);// 销毁系统数据
void destroyEmployee(SL* employees);// 打印所有员工信息
void printEmployees(SL* employees);

程序主函数

#define _CRT_SECURE_NO_WARNINGS 1#include "SeqList.h"
#include "employee.h"int main()
{SL employees;SLInit(&employees);char ans = 0;printf("是否需要导入上次的数据:y/n");scanf(" %c", &ans);if (ans == 'y'){LoadEmployee(&employees);}menu();printf("请输入选项:");int choice = 0;while (scanf("%d", &choice)){switch (choice){case 1:employeeInfoGet(&employees);break;case 2:findEmployee_print(&employees);break;		case 3:modifyEmployee(&employees);break;case 4:insertEmployee(&employees);break;case 5:deleteEmployee(&employees);break;case 6:sortEmployees(&employees);break;case 7:printEmployees(&employees);break;case 8:destroyEmployee(&employees);printf("谢谢使用");return 1;default:printf("请按照菜单重新输入\n");break;}menu();printf("请输入选项:");}
}

5. 功能实现

5.1 加载数据

在加载数据函数中,使用fopen函数和fread函数进行文件操作,当fread文件读到一组数据时,向顺序表中插入一组数据,再循环读取直到读到文件结尾

// 导入数据
// 加载上一次的数据
void LoadEmployee(SL* employees) 
{FILE* pf = fopen("employees.txt", "rb"); if (pf == NULL) {perror("fopen error!\n"); return;}employee tmp;while (fread(&tmp, sizeof(employee), 1, pf)){SLPushBack(employees, tmp);}fclose(pf);printf("成功导入历史数据\n");
}

5.2 输入数据

首先通过num控制输入的员工个数

int num = 0;
printf("请输入员工个数:");
scanf("%d", &num);

接着通过for循环根据num的值控制数据的输入,对于生日的输入来说,为了保证生日日期的合法性,使用if语句和goto语句处理不正确的日期,输入完一组数据后,调用顺序表的尾插函数向顺序表中插入数据

for (int i = 0; i < num; i++)
{employee tmp;printf("请输入第%d个员工\n", i + 1);printf("请输入员工id:");scanf("%d", &(tmp.id));printf("请输入姓名:");scanf("%s", tmp.name);printf("请输入员工性别:");scanf("%s", tmp.gender);printf("请按照年月日的顺序以空格间隔输入员工生日:");
setBirth:scanf("%d%*c%d%*c%d", &(tmp.birthday.year),&(tmp.birthday.month),&(tmp.birthday.day));if (tmp.birthday.month < 1 || tmp.birthday.month > 12 ||(tmp.birthday.day < 1 ||tmp.birthday.day > GetMonthDays(tmp.birthday.year, tmp.birthday.month))){printf("请重新输入生日\n");goto setBirth;}printf("请输入员工学历:");scanf("%s", tmp.qualification);printf("请输入员工职业:");scanf("%s", tmp.job);printf("请输入员工电话:");scanf("%s", tmp.teleNum);printf("请输入员工的住址:");scanf("%s", tmp.location);SLPushBack(employees, tmp);
}

完整模块代码

// 获取日期函数
int GetMonthDays(int year, int month)
{int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))){return monthDays[month] + 1;}return monthDays[month];
}// 获取输入
void employeeInfoGet(SL* employees)
{int num = 0;printf("请输入员工个数:");scanf("%d", &num);for (int i = 0; i < num; i++){employee tmp;printf("请输入第%d个员工\n", i + 1);printf("请输入员工id:");scanf("%d", &(tmp.id));printf("请输入姓名:");scanf("%s", tmp.name);printf("请输入员工性别:");scanf("%s", tmp.gender);printf("请按照年月日的顺序以空格间隔输入员工生日:");setBirth:scanf("%d%*c%d%*c%d", &(tmp.birthday.year),&(tmp.birthday.month),&(tmp.birthday.day));if (tmp.birthday.month < 1 || tmp.birthday.month > 12 ||(tmp.birthday.day < 1 ||tmp.birthday.day > GetMonthDays(tmp.birthday.year, tmp.birthday.month))){printf("请重新输入生日\n");goto setBirth;}printf("请输入员工学历:");scanf("%s", tmp.qualification);printf("请输入员工职业:");scanf("%s", tmp.job);printf("请输入员工电话:");scanf("%s", tmp.teleNum);printf("请输入员工的住址:");scanf("%s", tmp.location);SLPushBack(employees, tmp);}
}

5.3 查找员工信息

在设计查找模块时,一共实现了两种查找方式:

  1. 查找返回下标
  2. 查找打印员工信息
5.3.1 查找返回下标

调用返回下标函数便于删除函数调用,一共实现了两种查找方式

  1. 按照名字查找
// 按照名字查找
int findBy_name(SL* employees, char* target)
{for (int i = 0; i < employees->size; i++){if (strcmp(employees->SeqList[i].name, target) == 0){return i;// 返回对应位置下标}}return -1;
}
  1. 按照id查找
// 按照id查找
int findBy_id(SL* employees, int target)
{for (int i = 0; i < employees->size; i++){if (employees->SeqList[i].id == target){return i;// 返回对应位置下标}}return -1;
}

调用两种函数的主调函数

// 查找员工——返回下标
int findEmployee(SL* employees)
{int choice = 0;int pos = 0;menuForFind();printf("请选择查找方式:");while (scanf("%d", &choice)){switch (choice){case 1:{int target = 0;printf("请输入需要查找的员工id:");scanf("%d", &target);pos = findBy_id(employees, target);return pos;}break;case 2:{char name[50] = { 0 };printf("请输入需要查找的员工姓名:");scanf("%s", name);pos = findBy_name(employees, name);return pos;}break;case 3:return -1;default:menuForFind();printf("请重新按照菜单输入:");break;}menuForFind();printf("请选择查找方式:");}return -1;
}
5.3.2 查找打印员工信息

调用返回下标的函数,根据返回的pos下标打印对应的员工信息

// 查找并打印员工信息
void findEmployee_print(SL* employees)
{// 调用findEmployee函数int pos = findEmployee(employees);if (pos != -1){printf("\t员工id:%d", employees->SeqList[pos].id);printf("\t姓名:%s", employees->SeqList[pos].name);printf("\t性别:%s", employees->SeqList[pos].gender);printf("\t生日:%d-%02d-%02d\n", employees->SeqList[pos].birthday.year,employees->SeqList[pos].birthday.month,employees->SeqList[pos].birthday.day);printf("\t学历:%s", employees->SeqList[pos].qualification);printf("\t职业:%s", employees->SeqList[pos].job);printf("\t电话:%s", employees->SeqList[pos].teleNum);printf("\t地址:%s\n", employees->SeqList[pos].location);}else{printf("查无此人\n");}
}

5.4 修改员工信息

实现修改员工信息函数可以选择修改8种信息:

  1. 编号
  2. 姓名
  3. 性别
  4. 出生日期
  5. 学历
  6. 职业
  7. 电话
  8. 地址

对于生日日期的输入来说,存在合法日期判断,与输入处理方式类似,结合if语句和goto语句进行处理

[!IMPORTANT]
在下面的代码中,对于生日信息的修改使用了整体修改和判断而不是针对年、月或者日进行单独修改,原因如下:
以年为例,如果用户的生日所在年份是闰年,那么对应的2月有29天,但是当用户需要修改年为非闰年时,那么2月的日期也需要修改,此时需要对年、月和日都写判断条件,所以看似是修改一个年份,实际上个别情况需要两个变量都需要修改,所以为了使得修改方便,考虑整体修改再最后整体判断
// 修改员工
void modifyEmployee(SL* employees)
{// 调用查找函数找出需要修改的员工int pos = findEmployee(employees);if (pos == -1){printf("查无此人\n");return;}int choice = 0;menuForModify();printf("请选择需要修改的内容:");while (scanf(" %d", &choice)){switch (choice){case 1:printf("请输入需要修改的id:");scanf("%d", &(employees->SeqList[pos].id));printf("修改完成\n");break;case 2:printf("请输入需要修改的姓名:");scanf("%s", employees->SeqList[pos].name);printf("修改完成\n");break;case 3:printf("请输入需要修改的性别:");scanf("%s", employees->SeqList[pos].gender);printf("修改完成\n");break;case 4:{modifyBirth:printf("请输入修改后的年月日,以空格间隔:");scanf("%d%*c%d%*c%d", &(employees->SeqList[pos].birthday.year),&(employees->SeqList[pos].birthday.month),&(employees->SeqList[pos].birthday.day));if (employees->SeqList[pos].birthday.month < 1 || employees->SeqList[pos].birthday.month > 12 ||(employees->SeqList[pos].birthday.day < 1 ||employees->SeqList[pos].birthday.day > GetMonthDays(employees->SeqList[pos].birthday.year, employees->SeqList[pos].birthday.month))){printf("日期不合法,请重新输入生日\n");goto modifyBirth;}}printf("修改完成\n");break;case 5:printf("请输入需要修改的学历:");scanf("%s", employees->SeqList[pos].qualification);printf("修改完成\n");break;case 6:printf("请输入需要修改的职业:");scanf("%s", employees->SeqList[pos].job);printf("修改完成\n");break;case 7:printf("请输入需要修改的电话:");scanf("%s", employees->SeqList[pos].teleNum);printf("修改完成\n");break;case 8:printf("请输入需要修改的地址:");scanf("%s", employees->SeqList[pos].location);printf("修改完成\n");break;case 9:return;default:printf("请重新选择\n");break;}menuForModify();printf("请选择需要修改的内容:");}
}

5.5 插入员工信息

在插入员工信息函数中,因为与输入员工信息函数基本思路相同,所以考虑直接复用输入员工信息函数

// 插入数据
void insertEmployee(SL* employees)
{// 复用输入函数employeeInfoGet(employees);
}

5.6 删除员工信息

删除员工信息首先需要调用查找到指定员工,再执行删除,如果没找到,则提示“查无此人,删除失败”,否则“删除成功”,因为删除是删除顺序表中的元素,所以直接调用顺序表的删除函数

// 删除员工
void deleteEmployee(SL* employees)
{// 调用查找函数int pos = findEmployee(employees);if (pos < 0){printf("查无此人,删除失败");}SLErase(employees, pos);printf("删除完成\n");
}

5.7 排序员工信息

实现员工的排序信息一共有三种实现方式:

  1. 按照员工id排序
// 按照id排序
int cmp_id(const void* p1, const void* p2)
{return ((employee*)p1)->id - ((employee*)p2)->id;
}
  1. 按照员工姓名排序
// 名字比较
int cmp_name(const void* p1, const void* p2)
{return strcmp(((employee*)p1)->name, ((employee*)p2)->name);
}
  1. 按照员工生日日期排序
// 日期比较
int birthdayCmp(const void* p1, const void* p2)
{//如果年大就直接返回1if (((employee*)p1)->birthday.year > ((employee*)p2)->birthday.year){return 1;}else if (((employee*)p1)->birthday.year == ((employee*)p2)->birthday.year &&((employee*)p1)->birthday.month > ((employee*)p2)->birthday.month)//年相等时比较月份,月份大就直接返回true{return 1;}else if (((employee*)p1)->birthday.year == ((employee*)p2)->birthday.year &&((employee*)p1)->birthday.month == ((employee*)p2)->birthday.month && ((employee*)p1)->birthday.day > ((employee*)p2)->birthday.day)//年相等,月份相等时,天大就直接返回true{return 1;}else//其他情况均返回-1{return -1;}
}

在排序主调函数中,使用C语言库中的qsort函数结合函数指针调用上述三种函数完成对应的排序功能

调用三种函数的主调函数:

直接调用函数

// 排序主体
void sortEmployees(SL* employees)
{int choice = 0;menuForSort();printf("请选择需要排序的字段:");while (scanf("%d", &choice)){switch (choice){case 1:qsort(employees->SeqList, employees->size, sizeof(employee), cmp_id);printSortedEmp(employees->SeqList, employees->size);break;case 2:qsort(employees->SeqList, employees->size, sizeof(employee), cmp_name);printSortedEmp(employees->SeqList, employees->size);break;case 3:qsort(employees->SeqList, employees->size, sizeof(employee), birthdayCmp);printSortedEmp(employees->SeqList, employees->size);break;case 4:return;default:menuForSort();printf("请重新按照菜单输入:");break;}menuForSort();printf("请选择需要排序的字段:");}
}

转移表优化

// 转移表优化
void sortEmployees(SL* employees)
{int choice = 0;menuForSort();printf("请选择需要排序的字段:");// 定义函数指针数组int (*cmp[4])(const void*, const void*) = {0, cmp_id, cmp_name, birthdayCmp};//cmp arr[4] = {0, cmp_id, cmp_name, birthdayCmp};while (scanf("%d", &choice)){if (choice >= 1 && choice <= 3){qsort(employees->SeqList, employees->size, sizeof(employee), cmp[choice]);printSortedEmp(employees->SeqList, employees->size);}else if (choice == 4){break;}else{menuForSort();printf("请重新输入:");}menuForSort();printf("请选择需要排序的字段:");}
}

为了可以更好看到排序后的结果,在每一次排序结束后,将自动执行一次排序结果打印函数

// 打印排序后的内容
void printSortedEmp(employee* tmp, int size)
{for (int i = 0; i < size; i++){printf("第%d名员工:\n", i + 1);printf("\t员工id:%d", tmp[i].id);printf("\t姓名:%s", tmp[i].name);printf("\t性别:%s", tmp[i].gender);printf("\t生日:%d-%02d-%02d\n", tmp[i].birthday.year,tmp[i].birthday.month,tmp[i].birthday.day);printf("\t学历:%s", tmp[i].qualification);printf("\t职业:%s", tmp[i].job);printf("\t电话:%s", tmp[i].teleNum);printf("\t地址:%s\n", tmp[i].location);}
}

5.8 写入数据与顺序表空间释放

为了更好地保存数据,在程序结束时考虑将内存中的数据使用fopenfwrite函数写入硬盘中

// 向文件中写入数据
void writeIntoFile(SL* employees)
{FILE* pf = fopen("employees.txt", "wb"); if (pf == NULL) {perror("fopen error!\n"); return;}//将通讯录数据写⼊⽂件for (int i = 0; i < employees->size; i++){fwrite(employees->SeqList + i, sizeof(employee), 1, pf);}fclose(pf);printf("数据保存成功!\n");
}

在程序结束前,防止内存泄漏问题,需要对顺序表在堆上的空间进行释放,考虑设计空间销毁函数,因为需要写数据到文件中,所以写入文件过程可以放入空间释放函数中,通过询问用户是否需要保存数据实现更好的交互性

void destroyEmployee(SL* employees)
{char ans = 0;printf("是否需要保存数据:y/n");scanf(" %c", &ans);if (ans == 'y'){writeIntoFile(employees);}SLDestroy(employees);
}

5.9 打印所有员工信息

为了更好让用户看到已经保存到内存缓冲区的内容,考虑使用打印函数将缓冲区内容打印到控制台

// 打印所有员工信息
void printEmployees(SL* employees)
{// 没有员工直接返回if (employees->size == 0){printf("暂无数据\n");return;}for (int i = 0; i < employees->size; i++){printf("第%d名员工:\n", i + 1);printf("\t员工id:%d", employees->SeqList[i].id);printf("\t姓名:%s", employees->SeqList[i].name);printf("\t性别:%s", employees->SeqList[i].gender);printf("\t生日:%d-%02d-%02d\n", employees->SeqList[i].birthday.year,employees->SeqList[i].birthday.month,employees->SeqList[i].birthday.day);printf("\t学历:%s", employees->SeqList[i].qualification);printf("\t职业:%s", employees->SeqList[i].job);printf("\t电话:%s", employees->SeqList[i].teleNum);printf("\t地址:%s\n", employees->SeqList[i].location);}
}

这篇关于C语言实现员工管理系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1069601

相关文章

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

在线装修管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,装修队管理,用户管理,装修管理,基础数据管理,论坛管理 前台账户功能包括:系统首页,个人中心,公告信息,论坛,装修,装修队 开发系统:Windows 架构模式:B/S JDK版本:Java JDK1.8 开发工具:IDEA(推荐) 数据库版本: mysql5.7 数据库可视化工具: navicat 服务器:SpringBoot自带 ap

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

基于Springboot + vue 的抗疫物质管理系统的设计与实现

目录 📚 前言 📑摘要 📑系统流程 📚 系统架构设计 📚 数据库设计 📚 系统功能的具体实现    💬 系统登录注册 系统登录 登录界面   用户添加  💬 抗疫列表展示模块     区域信息管理 添加物资详情 抗疫物资列表展示 抗疫物资申请 抗疫物资审核 ✒️ 源码实现 💖 源码获取 😁 联系方式 📚 前言 📑博客主页:

探索蓝牙协议的奥秘:用ESP32实现高质量蓝牙音频传输

蓝牙(Bluetooth)是一种短距离无线通信技术,广泛应用于各种电子设备之间的数据传输。自1994年由爱立信公司首次提出以来,蓝牙技术已经经历了多个版本的更新和改进。本文将详细介绍蓝牙协议,并通过一个具体的项目——使用ESP32实现蓝牙音频传输,来展示蓝牙协议的实际应用及其优点。 蓝牙协议概述 蓝牙协议栈 蓝牙协议栈是蓝牙技术的核心,定义了蓝牙设备之间如何进行通信。蓝牙协议