资源描述
C语言课程设计报告-工资管理系统
一. 系统功能
1,输入记录模块主要完成将数据存入数组中的工作。记录可以从以二进制形式存储的数据文件中读入,也可以从键盘逐个输入记录。
2,查询记录模块主要完成在数组中查找满足相关条件的记录。在查询记录模块中,用户可以按照职工编号或者姓名在数组中进行查找。
3,更新记录模块主要完成对记录的维护。在此工资管理系统中,实现对记录的修改、删除、插入、排序操作。
4,统计记录模块主要完成对公司员工的工资在各等级的人数统计。
5,输出记录模块主要完成两个任务:
第一,它实现对记录的存盘操作,将数组中各元素的存储的记录信息写入数据文件中。
第二,它实现将数组中存储的记录信息以表格的形式在屏幕上打印出来。
6,从文件读入功能模块主要是将数据存放在数组中。该模块从数据文件中读取所有记录,通过多次调用fread()文件函数,每次操作都是从文件中读取一条工资记录信息存入数组中的操作.
7,从键盘输入功能模块主要实现记录从键盘上输入Add函数实现。首先把文件中存在的记录全部显示出来,而后从键盘接收多次输入记录,如果职工编号重复则提醒重新输入,输入的记录暂保存到结构体数组中,输入0回到主菜单。
8,查询模块主要实现在结构体数组中按职工编号或姓名查找满足条件的记录的功能,通过Qur()函数来实现。提供两种方式查询:1为按照编号来查询,2按照姓名来查询;如找到查询内容则输出记录值,找不到则提示无此项记录。
9,更新记录模块主要是实现对记录的修改、删除、插入、排序操作。所有这些操作都在数组中完成。
修改记录操作需要对数组中目标元素的数据域中的值进行修改,分三步完成:首先显示所有的记录,然后输入要修改的职工编号,调用Locate()函数定位该职工的编号记录,第三步,若找到该记录,则修改职工编号之外的各字段的值。
10,删除记录操作完成删除制定的职工编号或者姓名的记录。也分三步完成:首先显示所有的记录,而后输入要删除的职工编号或者姓名,输入后调用Locate()函数定位该职工的记录,第三步,若找到该记录,则从该记录所在元素的后续元素起,依次向前移一个元素位置,元素个数减1。
11,插入记录操作完成在指定职工编号的随后位置插入新的记录。首先,它要求用户输入某个员工的职工编号,新的记录将插入在该记录之后;然后提示用户输入一条新的记录信息。
12,排序操作主要实现按照实发工资降序排序。分两步,首先输出所有的记录,而后按照冒泡排序法的思想进行排序,输出排序的结果。
13,统计记录模块的实现比较简单,首先输出所有的记录,而后根据实发工资进行判断,完成工资在各个等级的人数统计。
14,输出至文件模块是把保存在数组中的记录保存到文件中。在Save函数中实现,当把记录输入至文件时,调用fwrite( )函数,即将数组元素中各字段的值都写入文件中。
15,输出至屏幕模块是把保存在数组中的记录显示到屏幕上,调用Disp( )函数,以表格的形式显示所有的记录。
二. 总体设计
1、数据结构的设计
定义结构体emolyee,用于存放职工的基本信息和工资信息。
typedef struct empoyee
{
char num[10]; 保存职工编号
char name[15]; 保存职工姓名
int jbgz; 保存职工基本工资
int jj; 保存职工奖金
int kk; 保存职工扣款
int yfgz; 保存职工应发工资
int sk; 保存职工税款
int sfgz; 保存职工实发工资
}ZGGZ;
定义Saveflag 全局变量,用于存放结构体数组是否修改过。1为改过,2未改过
2、函数设计及功能描述
1,int Add(ZGGZ TP[],int n )
函数用于在数组tp中增加工资记录元素,并返回数组中的当前记录数i。若在刚进入工资管理系统时数据文件为空,则将从数组的头部开始增加记录;否则,将记录添加在数组的尾部
2,void BoFang(int s);
通过传入不同的声音文件编号来播放不同的XP的系统声音
网上找的模板
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "Winmm.lib")
void main()
{
PlaySound(TEXT("C:\\WINDOWS\\Media\\WindowsXP启动.wav"),NULL,SND_FILENAME|SND_SYNC);
}
3, int Del(ZGGZ tp[],int n);
函数用于先在数组tp中找到满足条件的记录,然后删除该记录,并返回数组中的当前记录数
支持选择按照编号查找和按照姓名查找
4,void Disp(ZGGZ tp[],int n);
先调用PrintHeader()显示表头,再用for循环调用PrintData来显示结构体数组中每个记录的信息
5,void FunTitle(int m);
功能是截获用户在主菜单所做的选择,并在进入功能前提示用户当前所选择的功能
6,void Huang(char str[],int max,int ciShu);
一个自创小程序,利用Dos命令cls刷屏可以让字符串在显示器上左右摇曳
通过输出空格的数量控制位置,通过cls刷屏命令来清除上次的显示,达到移动效果
7,int Insert(ZGGZ tp[],int n);
函数用于在数组tp中插入记录,并返回数组中的当前记录数, 支持选择按照编号查找
8,void JiSuan(ZGGZ *p);
计算实际工资,并通过指针直接修改数组的值
9,int Locate(ZGGZ tp[],int n,char findmess[],int nameornum);
函数用于定位数组中符合要求的元素,并返回该数组元素的下标值, 支持选择按照编号查找和按照姓名查找
参数findmess[]保存要查找的内容,nameornum为1name,2num
10,void Menu();
显示主菜单
11,void Message(int n);
显示提示信息
12,void Modify(ZGGZ tp[],int n);
函数用于在数组tp中查找和修改记录元素, 支持选择按照编号查找和按照姓名查找
13,void NoFound(int n);
提示未找到信息
14,float NumberInput(int notice);
函数用于按需要输入float数值型数据
15,void PrintData(ZGGZ pp);
函数用于以表格显示的方式,打印输出单个数组元素中的记录信息
16,void PrintHeader();
函数用于在以表格形式显示,打印输出表头信息
17,void Qur(ZGGZ tp[],int no);
函数用于在数组tp中按职工编号或者姓名查找满足条件的记录,并显示记录, 支持选择按照编号查找和按照姓名查找
18,void Save(ZGGZ TP[],int n);
将修改好的结构体数组用wb的写入方式写到指定的磁盘文件中保存下来
19,void Sort(ZGGZ tp[],int n);
函数用于在数组tp中完成利用冒泡排序法实现数组的按实发工资字段的降序排序
20,void StringInput(char *t,int cho);
函数用于输入字符串,并进行字符串长度验证(长度小于设定值),cho为5工号或6姓名
cho==5工号为8个字节,cho==6姓名为12个字节
按照老师建议用了fflush(stdin); 来清除输入缓冲区
21,void TongJi(ZGGZ tp[],int n);
函数用于在数组tp中完成记录的统计工作,分等级统计职工工资的整体分布情况并以表格的形式显示出来
22,void Wrong(int n);
函数用于显示错误语句
23,int YesOrNo();
函数用于获取用户输入的yes或者no的选择并相应返回1或0
24,void main()
主函数
三.函数分析
1. 函数的原型和参数说明
int Add(ZGGZ tp[],int i);//传入要增加记录的结构体数组的地址和总的记录的个数
void BoFang(int s);//传入要播放的声音对应的序号
int Del(ZGGZ tp[],int n);//传入要删除记录的结构体数组的地址和总的记录的个数
void Disp(ZGGZ tp[],int n);;//传入要显示记录的结构体数组的地址和总的记录的个数
void FunTitle(int m);//传入在主菜单所做的选择的序号
void Huang(char str[],int max,int ciShu);//传入要显示的字符串的地址,显示的最大屏幕位置,显示次数
int Insert(ZGGZ tp[],int n); //传入要插入记录的结构体数组的地址和总的记录的个数
void JiSuan(ZGGZ *p); //计算实际工资,参数为要计算的结构体数组元素的地址值
int Locate(ZGGZ tp[],int n,char findmess[],int nameornum); //传入要删除记录的结构体数组的地址,要查找的字符串,选择的是名字还是编号
void Menu();//主函数
void Message(int n);//显示消息对应的编号
void Modify(ZGGZ tp[],int n); //传入要修改记录的结构体数组的地址和总的记录的个数
void NoFound(int n);//未找到提示信息的编号
float NumberInput(int notice);//对调用哪个数据输入的选择
void PrintData(ZGGZ pp);//传入要输出的结构体数组元素的地址
void PrintHeader();//打印表格的表头
void Qur(ZGGZ tp[],int no); //传入要查找的记录的结构体数组的地址和总的记录的个数
void Save(ZGGZ TP[],int n); //传入要保存的结构体数组的地址和总的记录的个数
void Sort(ZGGZ tp[],int n); //传入要排序的结构体数组的地址和总的记录的个数
void StringInput(char *t,int cho);//传入要输入的字符串数组保存的地址
void TongJi(ZGGZ tp[],int n); //传入要进行统计的结构体数组的地址和总的记录的个数
void Wrong(int n);//传入错误消息的对应编号
int YesOrNo();//返回Y或N的选择
2. 函数的流程图(选择若干函数算法,绘制函数流程图)
开始
提示是按编号查询还是名字查询
编号?
结束
N
Y
N
Y
数组内容有?
调用Locate函数定位记录
输出找到记录
找到?
返回主菜单
Y
N
调用Locate函数定位记录
找到?
输出找到记录
N
Y
调用StringInput函数输入查询编号
调用StringInput函数输入查询姓名
Qur函数流程图
开始
调用Disp函数显示所用记录
输入为0?
调用StringInput函数输入编号flag为0
输入的内容保存到结构体数组中,n++
进入while(1)循环
结束
返回n
Y
Y
flag为1?
N
Y
与已有结构体数组元素比较
是否重复?
Y
退出循环,设flag为1
循环结束?
显式退出循环
N
进入while(1)循环
提示错误,是否重新输入?
Y
N
返回n结束
N
Add函数流程图
开始
提示是按编号删除还是名字删除
编号?
结束
N
Y
N
Y
数组内容有?
调用Locate函数定位记录
删除记录
找到?
返回主菜单
Y
N
调用Locate函数定位记录
找到?
删除记录
调用StringInput函数输入删除编号
调用StringInput函数输入删除姓名
Y
N
Del函数流程图
开始
调用Disp函数显示所有的记录
找到?
结束
N
Y
数组内容有?
返回主菜单
Y
N
调用StringInput函数输入修改编号
调用Locate函数定位记录
逐步修改数据
调用Disp函数显示所有的记录
Modify 函数流程图
开始
调用Disp函数显示所用记录
查询插入编号存在?
调用StringInput函数输入编号
输入的内容插入到结构体数组中该编号之后
进入while(1)循环
结束
回到主菜单
N
Y
重新输入?
N
Y
调用Disp函数显示所用记录
Insert函数流程图
3. 程序清单
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<io.h>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "Winmm.lib")
#define N 20
/*============================所有的函数声明分隔线====================================*/
typedef struct employee
{
char num[10]; //保存员工编号
char name[15]; //保存员工姓名
float jbgz; //保存员工基本工资
float jj; //保存员工奖金
float kk; //保存员工扣款
float yfgz; //保存员工应发工资
float sk; //保存员工税款
float sfgz; //保存员工实发工资
}ZGGZ;
/*================================函数分隔线====================================*/
int Add(ZGGZ tp[],int i);
void BoFang(int s);
int Del(ZGGZ tp[],int n);
void Disp(ZGGZ tp[],int n);
void FunTitle(int m);
void Huang(char str[],int max,int ciShu);
int Insert(ZGGZ tp[],int n);
void JiSuan(ZGGZ *p);
int Locate(ZGGZ tp[],int n,char findmess[],int nameornum);
void Menu();
void Message(int n);
void Modify(ZGGZ tp[],int n);
void NoFound(int n);
float NumberInput(int notice);
void PrintData(ZGGZ pp);
void PrintHeader();
void Qur(ZGGZ tp[],int no);
void Save(ZGGZ TP[],int n);
void Sort(ZGGZ tp[],int n);
void StringInput(char *t,int cho);
void TongJi(ZGGZ tp[],int n);
void Wrong(int n);
int YesOrNo();
int Saveflag=0;
void main()
{
printf("\t\t\t欢迎您进入员工工资管理系统\n");
printf("\t 员工工资管理系统正在启动中,请稍候……\n\n");
BoFang(27);
ZGGZ worker[N];
int len=0;
FILE *fp;
int n=0;
Message(1);
if((fp=fopen("C:\\record.dat","rb"))==NULL) //当record文件不存在则提示
{
NoFound(1);
Message(2);
if(YesOrNo()==0) goto End; //选择不新建则退出程序
else if(YesOrNo()==1) goto Meum; //选择新建
}
else
{
for(int i=0;!feof(fp);i++)
{
if(fread(&worker[i],sizeof(struct employee),1,fp)==1)
len++;
}
fclose(fp);
printf("\n\t 目前所有的公司员工工资记录数为%d个!\n",len);
}
Meum:Message(0);
while(1)
{
system("cls"); //Dos命令—刷屏
Menu();
n=(int)NumberInput(1);
FunTitle(n);
switch (n)
{
case 1:len=Add(worker,len); break;
case 2:Qur(worker,len); break;
case 3:Modify(worker,len); break;
case 4:len=Insert(worker,len); break;
case 5:len=Del(worker,len); break;
case 6:Sort(worker,len); break;
case 7:TongJi(worker,len); break;
case 8:Disp(worker,len); break;
case 9:Save(worker,len); break;
case 0:{
if(Saveflag==1)
{
Message(3); //是否要保存修改
if(YesOrNo()==1) Save(worker,len);
}
Message(4); //是否要退出
if(YesOrNo()!=0) goto End;
} break;
default:Wrong(1); break;
}
Message(0);
}
End:printf("\t正在退出员工工资管理系统……\n");
BoFang(20);
printf("\t感谢您使用本员工工资管理软件!\n");
char *p="版权米有,盗版不究!#^_^";
Huang(p,58,3);
}
/*================================函数分隔线====================================*/
void JiSuan(ZGGZ *p) //计算实际工资
{
(*p).yfgz=(*p).jbgz+(*p).jj-(*p).kk; //通过指针直接修改数组的值
double sk=0,yfgz=(*p).yfgz;
if(yfgz>100000) {sk+=45.0/100*(yfgz-100000); yfgz=yfgz-100000; }
if(yfgz>80000) {sk+=40.0/100*(yfgz-80000); yfgz=yfgz-80000; }
if(yfgz>60000) {sk+=35.0/100*(yfgz-60000); yfgz=yfgz-60000; }
if(yfgz>40000) {sk+=30.0/100*(yfgz-40000); yfgz=yfgz-40000; }
if(yfgz>20000) {sk+=25.0/100*(yfgz-20000); yfgz=yfgz-20000; }
if(yfgz>5000) {sk+=20.0/100*(yfgz-5000); yfgz=yfgz-5000; }
if(yfgz>2000) {sk+=15.0/100*(yfgz-2000); yfgz=yfgz-2000; }
if(yfgz>500) {sk+=10.0/100*(yfgz-500); yfgz=yfgz-500; }
if(yfgz>0) {sk+=5.0/100*(yfgz); }
(*p).sk=(float)sk;
(*p).sfgz=(*p).yfgz-(*p).sk;
}
/*================================函数分隔线====================================*/
void Disp(ZGGZ tp[],int n)
{ //函数用于显示数组中存储的n条记录,内容为employee中的内容
if(n==0) NoFound(2);
else
{
PrintHeader();
for(int i=0;i<n;i++)
PrintData(tp[i]);
}
}
/*================================函数分隔线====================================*/
float NumberInput(int notice) //函数用于输入数值型数据
{
float shuZhi=0;
printf("\n");
switch(notice)
{
case 1:printf("\t 请输入数字键0—9作个选择:"); break;
case 2:printf("\t 请输入数字键1或2作个选择:"); break;
case 3:printf("\t 请输入员工基本工资的金额:"); break;
case 4:printf("\t 请输入员工奖金的金额:\t"); break;
case 5:printf("\t 请输入员工扣款的金额:\t"); break;
}
printf("\t");
fflush(stdin);
scanf("%f",&shuZhi);
return shuZhi;
}
/*================================函数分隔线====================================*/
void StringInput(char *t,int cho) //cho为5工号或6姓名
{ //函数用于输入字符串,并进行字符串长度验证(长度小于lens)
int l=0;
int n=0;
do
{
if(n>0) Wrong(2);
n++;
Message(cho);
printf("\t");
fflush(stdin); //清除输入缓冲区
gets(t);
l=strlen(t);
}
while(l>=(cho==5?8:12)); //cho==5工号为8个字节,cho==6姓名为12个字节
}
/*================================函数分隔线====================================*/
int Locate(ZGGZ tp[],int n,char findmess[],int nameornum) //函数用于定位数组中符合要求的元素,并返回该数组元素的下标值
{
int i,m=0;
int count[N]={0};
char num[10];
switch(nameornum) //参数findmess[]保存要查找的内容,nameornum为1name,2num
{
case 1:
{
for(i=0;i<n;i++)
if(strcmp(tp[i].name,findmess)==0)
{
count[m]=i;
m++;
}
if(m>1)
{
printf("\t计算机已经找到%d项符合条件的职工记录!\n",m);
PrintHeader();
for(int a=0;a<m;a++)
PrintData(tp[count[a]]);
Message(23);
StringInput(num,5);
i=Locate(tp,n,num,2);
}
if(i==n) i=-1;
}
break;
case 2:
{
for(i=0;i<n;i++)
if(strcmp(tp[i].num,findmess)==0) goto Quit;
if(i==n) i=-1;
}break;
}
Quit:return i;
}
/*================================函数分隔线====================================*/
int Add(ZGGZ tp[],int i) //函数用于在数组tp中增加工资记录元素并返回数组中的当前记录数,n为记录数
{
ZGGZ tmp;
if(i>0) Disp(tp,i);
Message(17);
int flag=0;
while(1)
{
while(1)
{
Message(12);
StringInput(tmp.num,5);
flag=0;
if(strcmp(tmp.num,"0")==0) goto Quit;
for(int t=0;t<i;t++)
{
if(strcmp(tp[t].num,tmp.num)==0)
{
flag=1;
break;
}
}
if(flag==1)
{
Wrong(3);
if(YesOrNo()!=1) goto Quit;
}
else break;
}
strcpy(tp[i].num,tmp.num);
StringInput(tp[i].name,6);
tp[i].jbgz=NumberInput(3);
tp[i].jj=NumberInput(4);
tp[i].kk=NumberInput(5);
JiSuan(&tp[i]);
Message(7);
Saveflag=1;
i++;
}
Quit:return i;
}
/*================================函数分隔线====================================*/
void Save(ZGGZ TP[],int n)
{
FILE *fp;
fp=fopen("C:\\record.dat","wb");
for(int i=0;i<n;i++)
fwrite(&TP[i],sizeof(struct employee),1,fp);
fclose(fp);
Message(21);
BoFang(8);
}
/*================================函数分隔线====================================*/
int YesOrNo()
{
int n=2;
printf("\t");
char cho;
fflush(stdin);
cho=getchar();
if(cho=='y'||cho=='Y') n=1;
else if(cho=='n'||cho=='N') n=0;
printf("\n");
return n;
}
/*================================函数分隔线====================================*/
int Del(ZGGZ tp[],int n) //函数用于先在数组tp中找到满足条件的记录,然后删除该记录
{
Message(12); //输入0可返回主菜单
char str[20];
int cho;
if(n==0)
{ NoFound(3); goto End; }
Message(8);
int i; //用于返回下标
cho=(int)NumberInput(2); //做个选择
switch(cho)
{
case 1:StringInput(str,5);
i=Locate(tp,n,str,2);
if(i==-1) { NoFound(2); goto End; }
else
{
Message(11); //提示已经找到查找的员工的信息
PrintHeader();
PrintData(tp[i]);
Message(13); //提示是否确定要删除员工的工资信息
if(YesOrNo()==1)
{
BoFang(23);
strcpy(tp[i].num,tp[n-1].num); //用最后一个记录覆盖要删除的记录
tp[n-1].num[0]='\0';
strcpy(tp[i].name,tp[n-1].name);
tp[n-1].name[0]='\0';
tp[i].jbgz=tp[n-1].jbgz;
tp[n-1].jbgz=0.0;
tp[i].jj=tp[n-1].jj;
tp[n-1].jj=0.0;
tp[i].kk=tp[n-1].kk;
tp[n-1].kk=0.0;
tp[i].yfgz=tp[n-1].yfgz;
tp[n-1].yfgz=0.0;
tp[i].sk=tp[n-1].sk;
tp[n-1].sk=0.0;
tp[i].sfgz=tp[n-1].sfgz;
tp[n-1].sfgz=0.0;
Message(14);
Saveflag=1;
n--;
Disp(tp,n);
}
}break;
case 2:StringInput(str,6);
i=Locate(tp,n,str,1);
if(i==-1) { NoFound(2); goto End; }
else
{
Message(11); //提示已经找到查找的员工的信息
PrintHeader();
PrintData(tp[i]);
Message(13); //提示是否确定要删除员工的工资信息
if(YesOrNo()==1)
{
BoFang(23);
strcpy(tp[i].num,tp[n-1].num); //用最后一个记录覆盖要删除的记录
tp[n-1].num[0]='\0';
strcpy(tp[i].name,tp[n-1].name);
tp[n-1].name[0]='\0';
tp[i].jbgz=tp[n-1].jbgz;
tp[n-1].jbgz=0.0;
tp[i].jj=tp[n-1].jj;
tp[n-1].jj=0.0;
tp[i].kk=tp[n-1].kk;
tp[n-1].kk=0.0;
tp[i].yfgz=tp[n-1].yfgz;
tp[n-1].yfgz=0.0;
tp[i].sk=tp[n-1].sk;
tp[n-1].sk=0.0;
tp[i].sfgz=tp[n-1].sfgz;
tp[n-1].sfgz=0.0;
Message(14);
Saveflag=1;
n--;
Disp(tp,n);
}
}break;
}
End:return(n);
}
/*================================函数分隔线====================================*/
void Qur(ZGGZ tp[],int no) //函数用于在数组tp中按职工编号或者姓名查找满足条件的记录,并显示记录
{
char str[20];
int cho;
if(no==0)
{ NoFound(3); goto End; }
Message(8);
int i;
cho=(int)NumberInput(2);
switch(cho)
{
case 1:Message(10); //按员工的工号查询
StringInput(str,5);
i=Locate(tp,no,str,2);
i
展开阅读全文