题目描述:
编写一个程序来管理职工工资,系统能实现以下功能:
- 输入:职工信息的输入,并能自动计算职工的实发工资。
- 实发工资所有相加
- 要求先用记事本等编辑软件将各职工的基本信息编辑、保存在文件zhigong.dat中,运行程序后从文件中读取职工的基本信息;
- 修改:对职工信息进行修改;
- 增加:添加新职工信息;
- 删除:删除指定职工的信息;
- 查询:根据工号或姓名查询职工的信息;根据应发工资区间段查询某些职工的信息;根据籍贯查询职工的信息;
- 排序:根据工号对职工信息进行升序/降序排序;根据应发工资进行降序排序,若应发工资相同,年龄大的在前;根据实发工资进行降序排序,若实发工资相同,工号小的在前;
- 统计:根据职工年龄统计各年龄段(<=30,31~40,41~50,51~60)职工人数及占总人数的比例;根据职称统计各职称(高工,工程师,助理工程师)的人数等;
- 输出:输出所有职工信息或查询职工信息的结果;
- 保存:将编辑(增加、插入、删除、修改)后职工的所有信息保存到文件workers.dat中。
要求每个功能模块用一个函数实现。结构体数组或者单链表头指针在main函数中定义。
2.设计提示
(1)先确定职工信息的数据结构。如每个职工信息:工号、姓名、性别、出生日期、籍贯、职称、参加工作时间、应发工资、公积金、养老金、补贴、实发工资等。实发工资应该在输入过程中计算所得;
(2)划分实现职工工资管理的功能模块:如主菜单、输入数据、修改、查询、输出等功能,并确定各功能模块的实现算法;
(3)画出主要算法的流程图;
(4)选择C语言的技术:普通数组、结构体数组、函数、指针、单链表或文件等;
(5)编写程序代码。
功能模块:
算法分析
核心模块:添加、修改、删除、查询、排序、统计
添加流程图如下:
修改职工信息流程图如下:
排序流程图如下:
删除流程图如下:
运行结果截图测试:
输入1,进行添加功能,输入添加的人数,然后根据提示依次添加工号,姓名,性别,出生日期,籍贯,职称,参加工作时间,应发工资,公积金,养老金,补贴,实发工资;
输入7输出所有的员工信息:
输入2 进行修改操作,如下图所示 :
输入3,进行删除操作:
删除后,工号3的员工不存在:
文件中,验证工号为3的员工不存在:
输入4,进行查询功能,选择1,按工号查询,查询结果如下图:
选择2,按姓名查:
输入3,按工资区间查:
输入4,按籍贯去查:
排序:
选择1,根据工号进行降序排序,原先文件工号为1在前,排完之后工号为2的在前
按工号排序查:
对应的文件中即可看到排序:
按工资排序:
根据应发工资排序:
职称统计:
保存职工信息到文件:
此时需要,退出程序后
即可看到新保存的数据,否则不结束程序,exe程序会占用workers.dat文件,
若不退出,则会出现:
代码实现:
源代码访问链接https://docs.qq.com/doc/p/71239d69a76f56cf1521717ae6b22c27cf876f10
定义数组:
//定义一个职工
//工号、姓名、性别、出生日期、籍贯、职称、
//参加工作时间、应发工资、公积金、养老金、补贴、实发工资
typedef struct employee {
char jobnum[20];//工号
char name[20];//姓名
char sex[2];//性别
char birthdate[20];//出生日期
char place[30];//籍贯
char workname[20];//职称
char jointime[20];//参加工作时间
double shouldsal;//应发工资
double actsal;//实发工资
double publicsal;//公积金
double oldsal;//养老金
double allowance;//补贴
}employee;
//链表
//节点
typedef struct Node
{
struct employee emp; //职工信息
struct Node* Next; //指向下一个节点
}Node;
//创建头节点
Node* Head = NULL; //指向头节点,置为空
添加员工信息:
void Add(int n)
{
struct employee emp[100];
FILE *fp;
int i;
fp=fopen("zhigong.dat","a+");
if(fp==NULL)
{
printf("打开文件失败,请重试!\n");
getch();
fclose(fp);
exit(0);
}
printf("请输入全部职工的工号,姓名,性别,出生日期,籍贯,职称,参加工作时间,应发工资,公积金,养老金,补贴,实发工资;\n");
for(i=0;i<n;i++)
{
printf("----第%d个职工----\n", i+1);
printf("工号:");
scanf("%s",emp[i].jobnum);
printf("姓名:");
scanf("%s",emp[i].name);
printf("性别:");
scanf("%s",emp[i].sex);
printf("出生日期:");
scanf("%s",emp[i].birthdate);
printf("籍贯:");
scanf("%s",emp[i].place);
printf("职称:");
scanf("%s",emp[i].workname);
printf("参加工作时间:");
scanf("%s",emp[i].jointime);
printf("应发工资:");
scanf("%lf",&emp[i].shouldsal);
printf("实发工资:");
scanf("%lf",&emp[i].actsal);
printf("公积金:");
scanf("%lf",&emp[i].publicsal);
printf("养老金:");
scanf("%lf",&emp[i].oldsal);
printf("补贴:");
scanf("%lf",&emp[i].allowance);
//fprintf(fp,"%s %s % %s %s %s %s %.2lf %.2lf %.2lf %.2lf %.2lf\n",emp[i].jobnum,emp[i].name,emp[i].birthdate,emp[i].place,emp[i].workname,emp[i].jointime,emp[i].shouldsal,emp[i].actsal,emp[i].publicsal,emp[i].oldsal,emp[i].allowance);}
//写入数据
fprintf(fp,"%s %s %.2s %s %s %s %s %.2lf %.2lf %.2lf %.2lf %.2lf\n",emp[i].jobnum,emp[i].name,emp[i].sex,emp[i].birthdate,emp[i].place,emp[i].workname,emp[i].jointime,emp[i].shouldsal,emp[i].actsal,emp[i].publicsal,emp[i].oldsal,emp[i].allowance);}
fclose(fp);
printf("添加职工成功,请按任意键返回\n");
getch();
}
删除员工:
void Delete() {
//struct employee emp[100];
int c=0,i;
FILE *fp;
fp=fopen("zhigong.dat","r");
if(fp==NULL)
{
printf("打开文件失败!\n");
getch();
exit(0);
}
//把文件里的数据放到链表中
while (!feof(fp))
{
//读取文件中的一行数据到一个职工信息结构体中
struct employee emp;
fscanf(fp,"%s %s %s %s %s %s %s %lf %lf %lf %lf %lf\n",
emp.jobnum, emp.name, emp.sex, emp.birthdate, emp.place, emp.workname,
emp.jointime, &emp.shouldsal, &emp.actsal, &emp.publicsal, &emp.oldsal, &emp.allowance);
c++;
//创建链表节点并赋值
Node* p = (Node*)malloc(sizeof(Node));
p->emp = emp;
p->Next = NULL;
//将节点添加到链表尾部
if (Head == NULL)
{
Head = p;
}
else
{
Node* tail = Head;
while (tail->Next != NULL)
{
tail = tail->Next;
}
tail->Next = p;
}
}
fclose(fp);
//到链表之后通过链表来删除,再把链表里的数据写入到文件里
char gh[20];
//printf("请输入删除职工的工号:");
scanf("%s", gh);
// 遍历链表查找
Node* p = Head;
// 记录前一个节点pre,删除时方便操作
// 初始时pre即为Head
Node* pre = Head;
// 记录是否有找到该工号的职员信息
bool isFindemp = false;
while (p != NULL) {
if (strcmp(gh, p->emp.jobnum) == 0) { // 找到目标节点
isFindemp = true;
// 删除节点为头节点
if (p == Head) {
Head = p->Next; // 直接把Next指向Head
}
// 删除节点为尾节点
else if (p->Next == NULL) {
p = pre;
p->Next = NULL;
}
// 删除节点为中间节点
else {
pre->Next = p->Next;
}
printf("删除成功。\n\n");
free(p); // 释放目标节点内存
break;
}
pre = p;
p = p->Next;
}
if (!isFindemp) {
printf("工号输入有误,系统中暂无该职工信息,无法进行删除操作。\n\n");
}
//把删除了的链表更新到zhigong.dat中
Node* q = Head; // 新增指针 q
fp=fopen("zhigong.dat","w");
while (q != NULL) {
fprintf(fp, "%s %s %s %s %s %s %s %lf %lf %lf %lf %lf\n", q->emp.jobnum, q->emp.name, q->emp.sex, q->emp.birthdate, q->emp.place, q->emp.workname, q->emp.jointime, q->emp.shouldsal, q->emp.actsal, q->emp.publicsal, q->emp.oldsal, q->emp.allowance);
q = q->Next;
}
getch();
fclose(fp);
}
//链表删除完毕,再遍历链表,存储到workers.dat中
//FILE* fp = NULL;
//按照应发工资区间段 查询
void gzcx(int n)
{
struct employee emp[100];
double qj[10];//用户指定工资区间
int i;
int c = 0;
FILE *fp;
printf("请输入你要查询的应发工资区间,用空格分隔两区间:");
scanf("%lf%lf",&qj[0],&qj[1]);
fp=fopen("zhigong.dat","r");
if(fp==NULL)
{
printf("打开文件失败!\n");
getch();
exit(0);
}
while(!feof(fp)){
fscanf(fp,"%s %s %s %s %s %s %s %lf %lf %lf %lf %lf\n",emp[c].jobnum,emp[c].name,emp[c].sex,emp[c].birthdate,emp[c].place,emp[c].workname,emp[c].jointime,&emp[c].shouldsal
,&emp[c].actsal,&emp[c].publicsal,&emp[c].oldsal,&emp[c].allowance);
c++;
}
fclose(fp);
n=c;
for(i=0;i<c;i++)
{
//应发工资shouldsal ,根据用户输入的区间段来查询
if(emp[i].shouldsal>=qj[0]&&emp[i].shouldsal<=qj[1]){
printf("工号\t姓名\t性别\t出生日期 籍贯\t职称\t\t参加工作时间\t\t应发工资\t实发工资\t公积金\t养老金\t补贴\n");
printf("%s\t%s\t%.2s\t%s %s\t%s\t\t%s\t%.2lf\t%.2lf\t\t%.2lf\t\t%.2lf\t%.2lf\n",emp[i].jobnum,emp[i].name,emp[i].sex,emp[i].birthdate,emp[i].place,emp[i].workname,emp[i].jointime,emp[i].shouldsal
,emp[i].actsal,emp[i].publicsal,emp[i].oldsal,emp[i].allowance);
printf("按任意键返回\n");
getch();
fclose(fp);
return;
}
}
printf("未找到要查询雇员,请重试!\n");
getch();
fclose(fp);
return;
}
//按照工号排序
void ghSort(int n){
struct employee emp[100];
int i,j;
//struct employee* temp;//temp设为结构体指针,交换
FILE *fp;
int c = 0;
fp=fopen("zhigong.dat","r+");//要进行读写,为r+
if(fp==NULL)
{
printf("打开文件失败!\n");
getch();
exit(0);
}
while(!feof(fp)){
fscanf(fp,"%s %s %s %s %s %s %s %lf %lf %lf %lf %lf\n",emp[c].jobnum,emp[c].name,emp[c].sex,emp[c].birthdate,emp[c].place,emp[c].workname,emp[c].jointime,&emp[c].shouldsal
,&emp[c].actsal,&emp[c].publicsal,&emp[c].oldsal,&emp[c].allowance);
c++;//c的作用
}
fclose(fp);
n=c;
//通过工号进行升序排序并存储
//工号为字符串,比较用strcmp
//排序这里n-count count要记录的是总人数
for(i=0;i<c-1;i++)
{
for(j=0;j<c-1-i;j++){
if(strcmp(emp[j].jobnum,emp[j+1].jobnum)>0){
// temp=&emp[j];
// emp[j]=emp[j+1];
// emp[j+1]=*temp;//temp指针*解引用
struct employee temp = emp[j];
emp[j] = emp[j + 1];
emp[j + 1] = temp;
}
}
}
//更新记录 ,打开zhigong.dat
fp=fopen("zhigong.dat","w");
for(i=0;i<n;i++)
{
fprintf(fp,"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%.2lf\t%.2lf\t%.2lf\t%.2lf\t%.2lf\n"
,emp[i].jobnum,emp[i].name,emp[i].sex,emp[i].birthdate,emp[i].place
,emp[i].workname,emp[i].jointime,emp[i].shouldsal
,emp[i].actsal,emp[i].publicsal,emp[i].oldsal,emp[i].allowance);
}
fclose(fp);
printf("数据按照工号升序排序成功,请按任意键返回主菜单。\n");
getch();
return;
}
//根据应发工资进行排序
void shouldsalSort(int n){
struct employee emp[100];
int i,j;
// struct employee* temp;
FILE *fp;
int c = 0;
fp=fopen("zhigong.dat","r");
if(fp==NULL)
{
printf("打开文件失败!\n");
getch();
exit(0);
}
while(!feof(fp)){
fscanf(fp,"%s %s %s %s %s %s %s %lf %lf %lf %lf %lf\n",emp[c].jobnum,emp[c].name,emp[c].sex,emp[c].birthdate,emp[c].place,emp[c].workname,emp[c].jointime,&emp[c].shouldsal
,&emp[c].actsal,&emp[c].publicsal,&emp[c].oldsal,&emp[c].allowance);
c++;
}
fclose(fp);
//通过应发工资进行降序排序并存储
for(i=0;i<c-1;i++)
{
for(j=0;j<c-1-i;j++){
if(emp[j].shouldsal==emp[j+1].shouldsal){
//年龄大的在前(出生日期)
if(strcmp(emp[j].birthdate,emp[j+1].birthdate)>0){
//字符串大,年龄小,交换
struct employee temp = emp[j];
emp[j] = emp[j + 1];
emp[j + 1] = temp;
continue;
}
}
if(emp[j].shouldsal<emp[j+1].shouldsal){
struct employee temp = emp[j];
emp[j] = emp[j + 1];
emp[j + 1] = temp;
}
main函数: