#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_EMP 50 // 系统最多可管理的职工人数
#define FILE_PATH "worker.dat" // 数据文件路径及名称
// 职工信息结构体定义(与课程设计报告模板字段对应)
typedef struct {
char name[5]; // 职工姓名
char ID[20]; // 职工工号(作为唯一标识,用于查找、修改、删除)
int salary1; // 基本工资
int salary2; // 职务工资
int salary3; // 津贴
int sum; // 工资总和(由前三项计算得到)
} Worker;
Worker wo[MAX_EMP]; // 职工信息数组,存放所有职工的数据
int Number = 0; // 当前已录入的职工人数,初始为0
// 函数声明(按课程设计的功能模块划分)
void menu(); // 系统主菜单函数
void input(); // 1.录入职工信息模块
void search(); // 2.查找职工信息模块
void del(); // 3.删除职工信息模块
void stat(); // 4.工资统计模块
void modify(); // 5.修改职工信息模块
void sort(); // 6.工资排序模块
void bc(Worker* temp); // 辅助:将一条职工信息写入文件(二进制追加)
void saveAll(); // 辅助:将当前所有职工信息写入文件(覆盖写入)
void load(); // 辅助:从文件中读取职工信息到数组
int main() {
// 仅在 Visual Studio(MSVC)下执行,用于解决中文乱码问题
#ifdef _MSC_VER
system("chcp 936 >nul"); // 将控制台代码页设为 GBK,>nul 用于不显示命令输出
#endif
load(); // 程序启动时,从文件中加载历史职工数据
int choice; // 用户选择的功能编号
while (1) { // 主循环:一直显示菜单,直到用户选择退出
menu(); // 显示主菜单
printf("请选择功能(1-7): ");
// 读取用户输入的功能编号
// 如果输入的不是数字,scanf 会返回 0,此时需要清空输入缓冲区
if (scanf("%d", &choice) != 1) {
while (getchar() != '\n'); // 把缓冲区里的垃圾字符读掉
choice = 0; // 设为无效值,让下面 default 处理
}
switch (choice) {
case 1: input(); break; // 1. 录入职工信息
case 2: search(); break; // 2. 查找职工信息
case 3: del(); break; // 3. 删除职工信息
case 4: stat(); break; // 4. 工资统计
case 5: modify(); break; // 5. 修改职工信息
case 6: sort(); break; // 6. 工资排序
case 7: // 7. 退出系统
saveAll(); // 退出前把当前内存中的数据全部保存到文件
printf("数据已保存,系统退出!\n");
system("pause"); // 暂停让用户看到提示
return 0; // 结束程序
default: // 输入了 1-7 以外的数字或非数字
printf("输入错误,请选择1-7的功能!\n");
}
system("pause"); // 每次操作后暂停,方便用户查看结果
system("cls"); // 清屏,准备下一次菜单显示
}
}
// 系统主菜单函数
// 功能:在控制台打印操作菜单,供用户选择功能
void menu() {
printf("\n$$$$$$$$$$¥¥工资管理系统¥¥$$$$$$$$$$\n");
printf("1. 录入职工信息 2. 查找职工信息\n");
printf("3. 删除职工信息 4. 工资统计分析\n");
printf("5. 修改职工信息 6. 工资排序\n");
printf("7. 退出系统\n");
printf("====================================================\n");
}
// 1. 录入职工信息模块
// 功能:批量录入职工信息,检查工号唯一性,并将数据保存到数组和文件
void input() {
// 检查是否已经达到最大职工数量
if (Number >= MAX_EMP) {
printf("职工数量已达上限(%d人),无法继续录入!\n", MAX_EMP);
return;
}
int numberTemp; // 临时变量,保存用户要录入的职工数量
printf("请输入要录入的职工数量: ");
scanf("%d", &numberTemp);
// 校验录入数量是否合法:
// 1)不能小于等于0
// 2)不能让当前总人数超过 MAX_EMP
if (numberTemp <= 0 || Number + numberTemp > MAX_EMP) {
printf("录入数量错误(数量需大于0且不超过剩余容量),请重新输入!\n");
return;
}
Worker temp; // 临时结构体变量,用来保存当前录入的一条职工信息
for (int i = 0; i < numberTemp; i++) {
printf("\n请输入第%d个职工信息\n", i + 1);
printf("姓名: "); scanf("%19s", temp.name); // 限制长度,防止数组越界
printf("工号: "); scanf("%19s", temp.ID);
// 检查工号是否已存在(唯一性检查)
int isRepeat = 0; // 0表示未重复,1表示重复
for (int j = 0; j < Number; j++) {
if (strcmp(wo[j].ID, temp.ID) == 0) {
isRepeat = 1; // 发现重复工号
break;
}
}
if (isRepeat) {
printf("工号 %s 已存在,该职工录入失败!请重新输入该职工信息。\n", temp.ID);
i--; // 让循环多执行一次,重新录入当前这条
continue;
}
printf("基本工资: "); scanf("%d", &temp.salary1);
printf("职务工资: "); scanf("%d", &temp.salary2);
printf("津贴: "); scanf("%d", &temp.salary3);
// 计算工资总和
temp.sum = temp.salary1 + temp.salary2 + temp.salary3;
// 将当前职工信息存入数组
wo[Number++] = temp;
// 将当前职工信息写入文件(以追加方式)
bc(&temp);
printf("第%d个职工信息录入成功!\n", i + 1);
}
}
// 2. 查找职工信息模块
// 功能:支持按姓名或工号查找,并输出匹配职工的详细信息
void search() {
// 如果当前没有职工信息,直接提示
if (Number == 0) {
printf("暂无职工信息,无法查找!\n");
return;
}
int select; // 查找方式选择:1-按姓名,2-按工号
printf("请选择查找方式:1.按姓名 2.按工号: ");
scanf("%d", &select);
char key[20]; // 保存用户输入的查找关键字
printf("请输入查找关键词: ");
scanf("%19s", key);
int findFlag = 0; // 是否找到匹配记录的标志,0表示未找到
printf("\n=====查找结果=====\n");
// 遍历所有职工信息,进行匹配
for (int i = 0; i < Number; i++) {
// 根据用户选择的查找方式进行比较
if ((select == 1 && strcmp(wo[i].name, key) == 0) ||
(select == 2 && strcmp(wo[i].ID, key) == 0)) {
// 找到匹配记录,输出详细信息
printf("姓名: %s\n工号: %s\n基本工资: %d\n职务工资: %d\n津贴: %d\n工资总和: %d\n",
wo[i].name, wo[i].ID, wo[i].salary1, wo[i].salary2, wo[i].salary3, wo[i].sum);
printf("-----------------\n");
findFlag = 1; // 标记为已找到
}
}
// 如果遍历结束仍然没有找到匹配记录
if (!findFlag) {
printf("未找到匹配的职工信息!\n");
}
}
// 3. 删除职工信息模块
// 功能:支持按姓名或工号删除职工信息,并更新文件
void del() {
if (Number == 0) {
printf("暂无职工信息,无法删除!\n");
return;
}
int select; // 删除方式:1-按姓名,2-按工号
printf("请选择删除方式:1.按姓名 2.按工号: ");
scanf("%d", &select);
char key[20]; // 删除关键字
printf("请输入删除关键词: ");
scanf("%19s", key);
int delCount = 0; // 记录被删除的记录条数
// 遍历数组,查找并删除匹配记录
for (int i = 0; i < Number; i++) {
if ((select == 1 && strcmp(wo[i].name, key) == 0) ||
(select == 2 && strcmp(wo[i].ID, key) == 0)) {
// 从当前位置开始,后面的记录依次向前移动一位,覆盖要删除的记录
for (int j = i; j < Number - 1; j++) {
wo[j] = wo[j + 1];
}
Number--; // 总人数减 1
delCount++; // 删除计数加 1
i--; // 回退下标,因为当前位置已经变成了下一条记录,需要重新检查
}
}
if (delCount > 0) {
saveAll(); // 删除后将剩余数据重新写入文件(覆盖写)
printf("成功删除 %d 条职工信息!\n", delCount);
}
else {
printf("未找到匹配的职工信息,删除失败!\n");
}
}
// 4. 工资统计模块
// 功能:显示每个职工的工资总和(也可以扩展为统计总和、平均等)
void stat() {
if (Number == 0) {
printf("暂无职工信息,无法统计!\n");
return;
}
printf("\n=====职工工资统计结果=====\n");
// 遍历所有职工,重新计算并显示工资总和
for (int i = 0; i < Number; i++) {
wo[i].sum = wo[i].salary1 + wo[i].salary2 + wo[i].salary3;
printf("%s(工号:%s)的工资总和为:%d\n", wo[i].name, wo[i].ID, wo[i].sum);
}
printf("==========================\n");
}
// 5. 修改职工信息模块
// 功能:按工号查找职工,修改其姓名和各项工资,并更新文件
void modify() {
if (Number == 0) {
printf("暂无职工信息,无法修改!\n");
return;
}
char id[20]; // 要修改职工的工号
printf("请输入要修改的职工工号: ");
scanf("%19s", id);
int idx = -1; // 保存找到的职工下标,初始为 -1(表示未找到)
// 遍历数组查找对应工号
for (int i = 0; i < Number; i++) {
if (strcmp(wo[i].ID, id) == 0) {
idx = i;
break;
}
}
if (idx == -1) {
printf("未找到该工号的职工,修改失败!\n");
return;
}
// 找到职工后,重新输入其信息(工号保持不变)
printf("\n请重新输入 %s 的信息(工号不可修改)\n", wo[idx].name);
printf("姓名: "); scanf("%19s", wo[idx].name);
printf("基本工资: "); scanf("%d", &wo[idx].salary1);
printf("职务工资: "); scanf("%d", &wo[idx].salary2);
printf("津贴: "); scanf("%d", &wo[idx].salary3);
// 重新计算工资总和
wo[idx].sum = wo[idx].salary1 + wo[idx].salary2 + wo[idx].salary3;
saveAll(); // 修改后将所有数据重新写入文件
printf("职工信息修改成功!\n");
}
// 6. 工资排序模块
// 功能:使用冒泡排序算法,按工资总和进行升序排序,并输出排序结果
void sort() {
if (Number <= 1) {
printf("职工数量不足,无需排序!\n");
return;
}
// 冒泡排序:按工资总和升序排列
for (int i = 0; i < Number - 1; i++) {
for (int j = 0; j < Number - 1 - i; j++) {
if (wo[j].sum > wo[j + 1].sum) {
// 交换两条记录
Worker k = wo[j];
wo[j] = wo[j + 1];
wo[j + 1] = k;
}
}
}
printf("职工信息已按工资总和升序排序完成!\n");
// 排序后输出排序结果,方便用户查看
printf("\n=====排序后职工信息=====\n");
for (int i = 0; i < Number; i++) {
printf("姓名:%s 工号:%s 工资总和:%d\n", wo[i].name, wo[i].ID, wo[i].sum);
}
}
// 辅助函数:bc
// 功能:将一条职工信息以二进制追加方式写入文件
void bc(Worker* temp) {
// 以 "ab" 模式打开文件:二进制 + 追加
FILE* fp = fopen(FILE_PATH, "ab");
if (fp == NULL) {
printf("文件打开失败,数据保存失败!\n");
return;
}
// 将一条记录写入文件
fwrite(temp, sizeof(Worker), 1, fp);
fclose(fp); // 关闭文件
}
// 辅助函数:saveAll
// 功能:将当前所有职工信息写入文件(先写人数,再写数组数据)
void saveAll() {
// 以 "wb" 模式打开文件:二进制 + 写入(覆盖原有内容)
FILE* fp = fopen(FILE_PATH, "wb");
if (fp == NULL) {
printf("文件打开失败,数据保存失败!\n");
return;
}
// 先写入当前职工人数
fwrite(&Number, sizeof(int), 1, fp);
// 再写入所有职工信息
fwrite(wo, sizeof(Worker), Number, fp);
fclose(fp);
}
// 辅助函数:load
// 功能:从文件中读取职工人数和职工信息到数组
void load() {
// 以 "rb" 模式打开文件:二进制 + 读取
FILE* fp = fopen(FILE_PATH, "rb");
if (fp == NULL) {
printf("首次运行,未检测到历史数据文件\n");
return;
}
// 先读取职工人数
fread(&Number, sizeof(int), 1, fp);
// 再读取职工信息到数组
fread(wo, sizeof(Worker), Number, fp);
fclose(fp);
printf("成功加载 %d 条职工历史数据!\n", Number);
}