前言

大部分初学计算机的同学完成的第一个比较完整的程序就是管理系统,不过细分之后可能还有图书管理系统,停车场管理系统。在小黄看来,一个人独立地完成这样一个系统对于自己代码能力的整体提升是非常大的,是把之前所学都融汇贯通的一个实现,同时也是对自己调试的能力的一个锻炼。
        总而言之,如果有机会,大家可以自己试一试独立完成一个完成的系统。

本次代码已上传至gitee:职工管理系统(描述为 “ a system”)

目录

分析题目

确定函数,完成头文件

大体框架的构建

具体函数的实现

菜单

打印全部

查询信息

修改信息

删除信息

排序信息

统计函数

保存函数 / 读取函数

优化建议


分析题目

这是小黄选取的一个题目,对于完成类似的稍大型的题目,首先要做的第一件事情就是对每一个要求进行分析,确定需要哪些函数进行封装实现。可以发现,最开始小黄是准备以链表的形式存储每一个职工的数据,但是考虑到存在对职工的顺序进行排序的功能,因此使用数组会更加方便。

//  基本的菜单界面,包括
//  1.显示所有职工信息  2.按照工号或者姓名查询详细信息(姓名可能存在重复)
//  3.查询基本工资低于1k的    4.打印工资条
//  5.修改记录                6.删除记录
//  7.按照基本工资或实发工资排序
//  8.统计各项工资平均值,存入新文件
//
//  以“a”的形式,读取文件,从文件中读取已有的数据,并存入到链表之中
//      一个存入链表的函数
//
//  一个显示函数,显示所有的信息
//
//  一个查询函数,按照工号和姓名以及基本工资 查找
//
//  打印工资条
//
//  修改函数
//
//  删除函数
//
//  *排序函数(出现排序的情况,尽量考虑使用结构体数组而不是链表)
//
//  统计函数

确定函数,完成头文件

当确定好整个项目的框架之后就可以先写好需要哪些函数,函数的传参还有类型可以先全部写void类型,等之后根据具体的情况进行修改、补充、添加。下方是小黄已经完成之后头文件,头文件主要是函数的定义,一些声明等,以及库的引用声明,头文件使得代码的可读性大大提升,完成较大的项目时,大家可以多多尝试。

typedef struct person  // sum 和 all 在其他数据录入的时候计算,避免使用时重复计算
{char id[20];char name[20];int basic;int wage;int extra;int hospital;int fund;int water;int sum;int all;
}person;void menu(void); // 打印菜单,美化界面,优化用户体验void save(person**); // 保存所有职工的信息,属于小黄个人单独添加的部分void read(person**); // 从文件中读取信息,和save函数连用,使得项目在第二次打开后可以保留上次的信息void show(person**); // 打印所有已存入的职工信息person** search(int, person**, person**); // 辅助函数,依据不同方式查找职工(考虑到可能出现重名,但是职工号不可能相同的情况)void print(person**, person**); // 打印职工工资条void add(person**); // 添加新的信息void change(person**, person**); // 修改信息void del(person**, person**); // 删除信息void sort(person**); // 对员工信息排序(不同方式)void statistic(double*, person**); // 对整体数据进行统计int cmp1(const void* a, const void* b);int cmp2(const void* a, const void* b);int cmp3(const void* a, const void* b);int cmp4(const void* a, const void* b); // 采用 qsort 进行排序,因此需要不同的 cmp 函数

大体框架的构建

完成此类程序,需要提前构建好一个主函数框架,想好一些测试案例,对于每一个函数写完就立马进行测试,防止连锁式错误。

例如小黄用的结构体指针数组,设置了三个成员直接加入,如此就方便进行一些测试,例如删除,打印所有信息等。然后设置一个循环,使得能多次操作。不过小黄个人建议,不要急着将菜单函数写入到主函数之中,然后再用 switch 语句分割,这样导致需要多次操作,是一件相对比较耽误时间的事情,甚至循环也不必要,先对每一个函数进行测试,测试完成之后就先屏蔽掉,等之后所有的都完成之后再去完成 while 和 switch 的排版等。

下方是小黄完成整个程序之后的主函数部分,单独成源文件,将函数的定义放到另一个源文件中,也是一种非常增加代码可读性的方式。

#include"function.h"
extern int way,nouse;int main()
{person* arr[1000] = { 0 }, * back[100] = { 0 };int key = 0;person a = { "3", "萨达", 426, 1246, 398, 189, 235, 165 , 5930, 531 };person b = { "2", "王小明", 486, 1246, 398, 189, 235, 165 , 5930, 5341 };person c = { "1", "南新", 428, 1246, 398, 189, 235, 165 , 5920, 51 };double sta[9] = { 0 };arr[0] = &a, arr[1] = &b, arr[2] = &c;read(arr);while (1){memset(back, 0, sizeof back);system("cls");menu();printf("请选择您要进行的操作:");nouse = scanf("%d", &key);switch(key){case 1: // 所有信息show(arr);break;case 2: // 查询信息printf("请选择您的查找方式:\n1.职工号\n2.姓名\n3.基本工资低于1000人员\n");nouse = scanf("%d", &way);search(way, arr, back);if (back[0] == NULL){system("cls");printf("暂无相关人员信息!\n");}else{show(back);}break;case 3: //打印工资条print(arr, back);break;case 4: // 增加信息add(arr);break;case 5: // 修改信息change(arr, back);if (back[0] == NULL){system("cls");printf("暂无相关人员信息!\n");}break;case 6: // 删除信息del(arr, back);if (back[0] == NULL){system("cls");printf("暂无相关人员信息!\n");}break;case 7: // 数据排序并输出 sort(arr);break;case 8: // 统计平均数statistic(sta, arr);break;case 0: // 退出系统save(arr);return 0; // 由于要跳出switch和while循环,且跳出之后没有操作,故直接returndefault:printf("wrong input!\n");}system("pause");}
}

具体函数的实现

菜单

代码如下,简单的打印,不需要过多解释,空格只根据黑框大小自行调整的居中位置。

void menu(void)
{printf("                                    ################################################\n");printf("                                    ###############                  ###############\n");printf("                                    ###############    职工管理系统  ###############\n");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("                                                       0.退出系统                  \n");

打印全部

先清空屏幕,打印一个表头,循环首先只能在1000的范围内,其次,打印遇到NULL的时候就可以退出循环,因为数据已经全部打印完;同时需要注意要是NULL指针的话,访问会出错,即注意判断的顺序。

void show(person** arr)
{system("cls");printf("%4s %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s\n","序号", "职工号", "姓名", "基本工资", "职务工资", "津贴", "医疗保险", "公积金", "水电费", "应发工资", "实发工资");for (int i = 0; i < 1000; i++){if (arr[i] == NULL){printf("\n");break;}printf("%4d %10s %10s %10d %10d %10d %10d %10d %10d %10d %10d\n", i + 1, arr[i]->id, arr[i]->name, arr[i]->basic, arr[i]->wage, arr[i]->extra, arr[i]->hospital, arr[i]->fund, arr[i]->water, arr[i]->sum, arr[i]->all);}
}

查询信息

查询到信息之后需要保存下来,同时还要决定是以什么来进行查找,也就是总共的参数有三个,查询方式 way,原数组 arr,记录返回的数组 back,之所以返回也使用数组储存是因为可能出现重名的情况,还有多个基本工资低于 1000 的职工,因此需要保存下来。同时优化体验,若没有找到符合条件的职工时,则输出 “ 暂无相关人员信息! ”;否则,则输出所有符合条件的职工的信息。

注意!查询的时候需要对 arr [ i ] 进行判断是否为空,然后判断内容是否符合筛选条件,先后顺序不能乱!因为若先判断是否符合条件的时候,若为空,那么则会出现访问异常的情况。

person** search(int way, person** arr, person** back)
{int n = 0;char check[20] = { 0 };switch (way){case 1:{int key = 0;printf("请输入所查询职工号:");nouse = scanf("%s", check);for (int i = 0; i < 1000; i++)if (arr[i] != NULL && strcmp(arr[i]->id, check) == 0){back[0] = arr[i];break;}break;}case 2:{printf("请输入所查询姓名:");nouse = scanf("%s", check);for (int i = 0; i < 1000; i++)if (arr[i] != NULL && strcmp(arr[i]->name, check) == 0)back[n++] = arr[i];break;}case 3:{for (int i = 0; i < 1000; i++)if (arr[i] != NULL && arr[i]->basic < 1000){back[n++] = arr[i];}break;}}return back;
}

修改信息

修改信息第一步就是先找到被修改人,那么调用 search 函数先找到所有符合要求的人,由于可能出现重名等情况,因此优化体验,使用户可以根据序号选择具体需要修改哪一个职工的信息,然后依次对每一条信息进行修改。

void change(person** arr, person** back)
{printf("请选择您的查找方式:\n1.职工号\n2.姓名\n3.基本工资低于1000人员\n");nouse = scanf("%d", &way);search(way, arr, back);if (back[0] != NULL){show(back);int target = 1;if (back[1] != NULL){printf("请选择您要修改的序号:");nouse = scanf("%d", &target);}printf("请依次输入:\n");printf(" 职工号: ");nouse = scanf("%s", (back[target - 1])->id);printf("  姓名:   ");nouse = scanf("%s", back[target - 1]->name);printf("基本工资:");nouse = scanf("%d", &(back[target - 1]->basic));printf("职务工资:");nouse = scanf("%d", &(back[target - 1]->wage));printf("  津贴:  ");nouse = scanf("%d", &(back[target - 1]->extra));printf("医疗保险:");nouse = scanf("%d", &(back[target - 1]->hospital));printf(" 公积金: ");nouse = scanf("%d", &(back[target - 1]->fund));printf(" 水电费: ");nouse = scanf("%d", &(back[target - 1]->water));back[target - 1]->sum = back[target - 1]->basic + back[target - 1]->wage + back[target - 1]->extra;back[target - 1]->all = back[target - 1]->sum - back[target - 1]->hospital - back[target - 1]->fund - back[target - 1]->water;printf("修改成功!\n");}
}

删除信息

由于 id 也就是职工号是必定不相同的,因此通过 search 找到需要删除的职工之后即可通过遍历 arr 数组并比较 id 来判断是否是需要删除的职工。

注意!注释掉的一行free需要加上!此处注释掉的原因是给出的三个测试用案例不是用 malloc 开辟的,所以无法 free 掉;同时由于删除必定会减少一个,为了防止越界,故循环只到 arr[ 998 ] ,而有可能出现数组恰好填满信息的情况,因此最后需要给 arr[ 999 ] 置空。

void del(person** arr, person** back)
{printf("请选择您的查找方式:\n1.职工号\n2.姓名\n3.基本工资低于1000人员\n");nouse = scanf("%d", &way);search(way, arr, back);if (back[0] != NULL){int target = 1;if (back[1] != NULL){show(back);printf("请选择您要删除的序号:");nouse = scanf("%d", &target);}memset(back[target - 1], 0, sizeof(struct person));if (back[target - 1] != 0){for (int i = 0; i < 1000; i++){if (!strcmp(arr[i]->id, back[target - 1]->id)){//free(arr[i]);for (int x = i + 1; x < 1000; x++){arr[x - 1] = arr[x];}arr[999] = NULL;break;}}printf("删除成功!\n");}elseprintf("删除失败!\n");}
}

排序信息

为了快捷,故使用了系统自带的 qsort 函数,只需要修改 cmp 函数即可,此时采用函数指针和 switch 函数构成的交换表进行,可自行修改 return 内两个指针的顺序来调整升序或降序。

void sort(person** arr)
{int (*cmp)(const void*, const void*) = &cmp1;printf("请选择您的排序方式:\n");printf("1.根据职工号排序:\n");printf("2.根据姓名排序:\n");printf("3.根据基本工资排序:\n");printf("4.根据实发工资排序:\n");int num = 0, nums = 0, i = 0;for (i = 0; i < 1000; i++)if (arr[i] == NULL || i == 1000)break;nouse = scanf("%d", &num);switch(num){ case 1:cmp = cmp1;break;case 2:cmp = cmp2;break;case 3:cmp = cmp3;break;case 4:cmp = cmp4;break;default:printf("wrong input!\n");return;}qsort(arr, i, sizeof(arr[0]), cmp);
}int cmp1(const void* a, const void* b)
{return strcmp((*(struct person**)a)->id, (*(person**)b)->id);
}int cmp2(const void* a, const void* b)
{return strcmp((*(person**)a)->name, (*(person**)b)->name);
}int cmp3(const void* a, const void* b)
{return (*(person**)a)->basic - (*(person**)b)->basic;
}int cmp4(const void* a, const void* b)
{return (*(person**)a)->all - (*(person**)b)->all;
}

统计函数

因为每次统计函数需要用户执行,且每次需要更新,因此以 “ w ” 的形式打开,计算方式是累加在平均。

void statistic(double* sta, person** arr)
{FILE* p = NULL;p = fopen("statistic.csv", "w");if (p == NULL)exit(0);for (int i = 0; i < 1000 && arr[i] != NULL; i++){sta[0]++;sta[1] += arr[i]->basic;sta[2] += arr[i]->wage;sta[3] += arr[i]->extra;sta[4] += arr[i]->hospital;sta[5] += arr[i]->fund;sta[6] += arr[i]->water;sta[7] += arr[i]->sum;sta[8] += arr[i]->all;}sta[1] /= sta[0];sta[2] /= sta[0];sta[3] /= sta[0];sta[4] /= sta[0];sta[5] /= sta[0];sta[6] /= sta[0];sta[7] /= sta[0];sta[8] /= sta[0];fprintf(p,"%s,%s,%s,%s,%s,%s,%s,%s,%s\n", "总人数", "平均基本工资", "平均职务工资", "平均津贴", "平均医疗保险", "平均公积金", "平均水电费", "平均应发工资", "平均实发工资");fprintf(p,"%0lf,%.2lf,%.02lf,%.2lf,%.2lf,%.2lf,%.2lf,%.2lf,%.2lf", sta[0], sta[1], sta[2], sta[3], sta[4], sta[5], sta[6], sta[7], sta[8]);system("cls");printf("平均统计:\n");printf("%10s %10s %10s %10s %10s %10s %10s %10s %10s\n", "总人数", "基本工资", "职务工资", "津贴", "医疗保险", "公积金", "水电费", "应发工资", "实发工资");printf("%10.0lf %10.2lf %10.02lf %10.2lf %10.2lf %10.2lf %10.2lf %10.2lf %10.2lf\n\n", sta[0], sta[1], sta[2], sta[3], sta[4], sta[5], sta[6], sta[7], sta[8]);fclose(p);
}

保存函数 / 读取函数

 保存时需要注意为空的情况和信息的最后一个,这两种情况都不需要添加换行符,这么做的原因和读取数据的函数有关,具体由下方解释。

读取信息的时候,需要先用一个字符串读取掉表头的文字,注意 csv 文件读取出来时,不同的单元格之间是用逗号分隔,因此会被 %s 读取到,故用一个字符串即可,然后用 fgetc 读取一个字符并记录下来,若为 \n 则表示后续还有信息,若为 \0 则表示已经到达文件结尾,退出循环。同时读取的时候由于 csv 文件读取方式的原因,采用了正则表达式, %[^,] 表示读取字符串直到遇到 " , " 停止。

将保存函数放到主函数的循环之外,即每次退出程序的时候自动保存,然后读取文件则是项目开始运行的时候自动读取,起到了优化体验的话。

void save(person** arr)
{FILE* p = NULL;p = fopen("data.csv", "w");if (p == NULL)exit(0);int i = 0;fprintf(p, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", "职工号", "姓名", "基本工资", "职务工资", "津贴", "医疗保险", "公积金", "水电费", "应发工资", "实发工资");if (arr[i] != NULL)fprintf(p, "\n");while (arr[i + 1] != NULL){fprintf(p, "%s,%s,%d,%d,%d,%d,%d,%d,%d,%d\n", arr[i]->id, arr[i]->name, arr[i]->basic, arr[i]->wage, arr[i]->extra, arr[i]->hospital, arr[i]->fund, arr[i]->water, arr[i]->sum, arr[i]->all);i++;}if (arr[i] != NULL)fprintf(p, "%s,%s,%d,%d,%d,%d,%d,%d,%d,%d", arr[i]->id, arr[i]->name, arr[i]->basic, arr[i]->wage, arr[i]->extra, arr[i]->hospital, arr[i]->fund, arr[i]->water, arr[i]->sum, arr[i]->all);
}void read(person** arr)
{FILE* p = NULL;int i = 0, judge = 1;;char k = 0;char nouse[100] = { 0 };p = fopen("data.csv", "r");if (p == NULL)return;judge = fscanf(p, "%s",  nouse);k = fgetc(p);while (k != EOF){person* node = (person*)malloc(sizeof person);arr[i] = node;judge = fscanf(p, "%[^,],%[^,],%d,%d,%d,%d,%d,%d,%d,%d", arr[i]->id, arr[i]->name, &arr[i]->basic, &arr[i]->wage, &arr[i]->extra, &arr[i]->hospital, &arr[i]->fund, &arr[i]->water, &arr[i]->sum, &arr[i]->all);k = fgetc(p);i++;}
}

优化建议

1. 考虑到快捷,本项目采用了静态表,但是可以进行优化,例如当数组满时,采用 realloc 开辟更大的空间。

2. 统计函数一块,可以添加大数运算的函数,防止人员过多时,可能出现的溢出现象。

3. 以上建议仅供参考,可以尝试改进,但是仅作为课程设计项目来说的话不太有必要,若使用中发现错误或不理解的地方欢迎私信小黄,也可私信QQ482999194。

【C语言】职工管理系统详解(文件操作)相关推荐

  1. C语言详解文件操作(一):文件操作基础概念、按照字符、按照行块、按照格式化和随机位置读写文件

    文章目录 一.文件操作基础概念 二.文件读写:按照字符方式读写 三.文件读写:按行和块读写 四.文件读写:格式化和随机位置 一.文件操作基础概念 C语言中的文件操作的好处:       一个文件通常是 ...

  2. python语言的格式框架_django框架模板语言使用方法详解

    本文实例讲述了django框架模板语言使用方法.分享给大家供大家参考,具体如下: 模板功能 作用:生成html界面内容,模版致力于界面如何显示,而不是程序逻辑.模板不仅仅是一个html文件,还包括了页 ...

  3. C语言再学习 -- 详解C++/C 面试题 2

    (经典)C语言测试:想成为嵌入式程序员应知道的0x10个基本问题. 参看:嵌入式程序员面试问题集锦 1.用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define ...

  4. C语言中编译预处理命令作用,C语言预处理命令详解

    原标题:C语言预处理命令详解 关注百问科技并将它设为星标 不错过任何一篇嵌入式干货 ------ 作者:clover_toeic 原文出处: https://www.cnblogs.com/clove ...

  5. 【C语言】字符变量详解

    [C语言]字符变量详解 作为计算机语言中的一种基本数据类型,字符型数据在编写计算机程序时经常用到.在C语言中,字符型数据是以char类型来表示的.在本篇博客中,我们将对C语言中的字符变量进行详细的讲解 ...

  6. 单片机c语言常用的语句有几条,单片机C语言常用语句详解

    <单片机C语言常用语句详解>由会员分享,可在线阅读,更多相关<单片机C语言常用语句详解(22页珍藏版)>请在人人文库网上搜索. 1.C51编程中常见语句的总结.首先,C51定义 ...

  7. C#利用ASP.NET?Core开发学生管理系统详解

    文章来源: 学习通http://www.bdgxy.com/ 普学网http://www.boxinghulanban.cn/ 智学网http://www.jaxp.net/ 表格制作excel教程h ...

  8. (转)C语言位运算详解

    地址:http://www.cnblogs.com/911/archive/2008/05/20/1203477.html C语言位运算详解 作者:911 说明:本文参考了http://www2.ts ...

  9. c语言练习题及答案)(1),c语言练习题(带详解答案)1.pdf

    c语言练习题(带详解答案)1 (-2) -1: 一单项选择题 /为求商运算符,该运算符能够对整型.字符.浮点等类型的数 据进行运算,5/2 2 1.(A )是构成C语言程序的基本单位. 11.如果 i ...

最新文章

  1. 网络地址转换(PAT)
  2. docker学习系列7 容器化Node项目
  3. Opengl-基本概念-转换矩阵坐标系(最难理解的两章)
  4. Win7 下打开wifi共享的方法
  5. 深度学习(三十三)——GAN参考资源
  6. vivado 使用DDS IP方法
  7. 通用即插即用监视器驱动下载_DirectX10下载|DirectX10 10.1 官方版
  8. 基于PHP的汉服文化交流平台 毕业设计-附源码240903
  9. python 直方图匹配_python库skimage 绘制直方图;绘制累计直方图;实现直方图匹配(histogram matching)...
  10. java编写蠕虫病毒_教大家编写蠕虫病毒
  11. Shawn邀您共读《WebGL编程指南》| GLSL ES篇
  12. Python量化分析,计算KDJ
  13. 电脑变时钟,防止消息游戏新闻的打扰,形成高效率的办公和学习
  14. Dubbo监控中心Dubbo-admin安装
  15. 字节跳动校招——运维工程师-系统架构岗位面经分享
  16. 一文说尽 MySQL 优化原理
  17. SpringBoot 重定向
  18. 【2020最新】人工智能实战就业(面试)学习路线图
  19. 【PaddleSeg】【天池大赛】真实场景篡改图像检测挑战赛线上2391
  20. 软件架构师 第一部分 基础篇 第六章 架构特性的范围

热门文章

  1. AnyCasting.2.4[铸造模拟软件最新+天喻CAD2005完美
  2. dw自动滚动图片_Dreamweaver向下滚动图片代码(不间断、带链接、鼠标指向暂停)-dreamweaver技巧-电脑技巧收藏家...
  3. 非接触式汽车喇叭滑环原理
  4. 第131章 SQL函数 SQUARE
  5. 【华为OD机试c++/java/python 真题2023 Q1】
  6. 利用openoffice将上传办公文档转换成PDF或者html
  7. 听说你的JWT库用起来特别扭,推荐这款贼好用的!
  8. oracle 12C没有hr_main.sql脚本文件,无法创建sample schema
  9. 基于人脸识别的课堂签到管理系统2020,7,21
  10. python网络爬虫权威指南 第2版 pdf微盘_python网络爬虫权威指南第2版pdf-Python网络爬虫权威指南第2版中文PDF+英文PDF+源代码下载_东坡手机下载...