北 京 林 業 大 學
2020學年—2021學年第1學期 程序設計基礎課程設計 實習報告書
實習內容: 高校教師工資管理系統
實習環境: Sublime Text 3
目錄
3.2.1 checkten()--對輸入的數字範圍進行檢測
3.2.3 checkid()--檢測用戶輸入id是否合法且合規
3.2.5 checkmon()--用以檢測所要查詢的月份是否存在salary信息中
3.3.1 inpter() inpsal()--ter輸入函數 與 sal輸入函數
3.3.2 delter() delsal()--刪除教師信息及薪資信息
3.4.1 showter()--可選mode的教師信息列表打印函數
3.4.2 searchterid() searchsalid()--尋找所傳id對應的實際索引以便後續操作
3.4.3 swapter()swapsal()swapcol()swapjob()--交換函數
3.4.4 sortter_id() sortsal_id()--排序函數
3.4.5 calcolavg_sort() caljobavg_sort()--實現統計分析排序功能的函數
3.4.6 FreshSalName()--同步sal與ter之間的信息
4.4.1根據月份,統計不同學院教師的平均應發工資和平均實發工資
4.4.2根據月份,統計不同職稱教師的平均應發工資和平均實發工資
一、實習步驟:
我於第一天進行該項目的整體分析及技術方案的選擇,第二天進行該程序的編碼工作,第三天進行程序的測試工作和報告的書寫工作。
二、實現方法概述:
根據要求,該程序完全基於C語言實現,未使用任何外部庫文件。代碼全部手敲完成,無任何借鑑複製行爲。且編寫代碼的全過程已錄製視頻,並將快放版上傳至B站:其地址爲:https://www.bilibili.com/video/BV1Af4y1e7Tc/
該程序按照實驗要求分爲五大模塊,完全實現實驗要求的所有功能。存儲在程序內採用結構體的方式,由於禁止使用SQL庫函數,所以我自己手寫了一套簡化版的SQL生成與解析函數,用於程序在本地的存儲結構(可實現對之前數據,或樣例數據的導入)。
設計採用自頂向下的方式,將每個大問題細化爲各個小問題。將需要重複用到的解決方案算法生成爲可選擇模式的函數(如4.0.1 showter()),以達到簡化代碼量且提高可移植性的目標。每完成一個小模塊便進行測試,以確保程序的正確性。
本系統系統功能結構
三、技術方案實現:
該程序的函數功能分爲四大模塊,接下來將一一介紹。
3.0 數據存儲結構
3.0.0本地存儲結構(**創新點**)
由於該項目禁止使用SQL數據庫的相關庫文件,我自己寫了一套類似於SQL存儲結構的生成函數及解釋函數。其於本地存儲的樣式類似於SQL中的insert values()語句。
- (0000000001,wu zheng,3,2)
- (0000000003,qian,3,2)
- (0000000001,wu zheng,3,2,2,200.00,100.00,56.00,300.00,244.00)
- (0000000001,wu zheng,3,2,3,45.00,6.00,6.00,51.00,45.00)
接下來將首先介紹該簡化SQL的生成方法
- void savetolocter()
- {
- // (id,name,colid,jobid)
- FILE * f1;
- f1=fopen("terdata.dat","w+");
- if(f1==NULL){printf("save to local unaviliable\n");return;}
- int i;
- for(i=0;i<tercnt;i++)
- {
- char tline[101]="(", tnum[5];
- strcat(tline,ter[i].id);
- strcat(tline,",");
- strcat(tline,ter[i].name);
- strcat(tline,",");
- strcat(tline,itoa(ter[i].collegeid,tnum,10));
- strcat(tline,",");
- strcat(tline,itoa(ter[i].jobid,tnum,10));
- strcat(tline,")");
- fprintf(f1, "%s\n", tline);
- }
- fclose(f1);
- }
- void savetolocsal()
- {
- FILE * f1;
- f1=fopen("saldata.dat","w+");
- if(f1==NULL){printf("save to local unaviliable\n");return;}
- int i;
- for(i=0;i<salcnt;i++)
- {
- char tline[201]="(", tnum[12];
- strcat(tline,sal[i].id);
- strcat(tline,",");
- strcat(tline,sal[i].name);
- strcat(tline,",");
- strcat(tline,itoa(sal[i].collegeid,tnum,10));
- strcat(tline,",");
- strcat(tline,itoa(sal[i].jobid,tnum,10));
- strcat(tline,",");
- strcat(tline,itoa(sal[i].month,tnum,10));
- strcat(tline,",");
- strcat(tline,d2s(sal[i].basicsal,tnum));
- strcat(tline,",");
- strcat(tline,d2s(sal[i].addsal,tnum));
- strcat(tline,",");
- strcat(tline,d2s(sal[i].subsal,tnum));
- strcat(tline,",");
- strcat(tline,d2s(sal[i].theosal,tnum));
- strcat(tline,",");
- strcat(tline,d2s(sal[i].truesal,tnum));
- strcat(tline,")");
- fprintf(f1, "%s\n", tline);
- }
- fclose(f1);
- }
其核心思想爲模擬SQL的數據以逗號爲分割的方式,將數據行通過fprintf結構化的存入到文件中,以便後續的讀取。
接下來將介紹該SQL語句的解析函數
- void loadlocter(char * loc)
- {
- FILE * f1;
- f1=fopen(loc,"r");
- if(f1==NULL){printf("load to here unaviliable\n");return;}
- tercnt=0;
- char tinp[100];
- while(fgets(tinp,300,f1)!=NULL && tinp[0]!=10)
- {
- char tid[11],tna[31];
- int tcd,tjd;
- int i;
- for(i=1;i<=10;i++) tid[i-1]=tinp[i];
- int tcnt=0;
- for(i=12;tinp[i]!=',';i++) tna[tcnt++]=tinp[i];
- tna[tcnt]='\0';
- tcd=atoi(&tinp[i+1]);
- tjd=atoi(&tinp[i+3]);
- strcpy(ter[tercnt].id,tid);
- strcpy(ter[tercnt].name,tna);
- ter[tercnt].collegeid=tcd;
- ter[tercnt].jobid=tjd;
- tercnt++;
- //(1234567890,de d,1,1)
- }
- fclose(f1);
- sortter_id();
- }
- void loadlocsal(char * loc)
- {
- FILE * f1;
- f1=fopen(loc,"r");
- if(f1==NULL){printf("load to here unaviliable\n");return;}
- salcnt=0;
- char tinp[200];
- while(fgets(tinp,300,f1)!=NULL && tinp[0]!=10)
- {
- char tid[11],tna[31];
- int tcd,tjd;
- double t1,t2,t3,t4,t5;
- int i;
- for(i=1;i<=10;i++) tid[i-1]=tinp[i];
- int tcnt=0;
- for(i=12;tinp[i]!=',';i++) tna[tcnt++]=tinp[i];
- tna[tcnt]='\0';
- tcd=atoi(&tinp[++i]);i++;
- tjd=atoi(&tinp[++i]);i+=2;
- char tmon[4];tcnt=0;
- for(;tinp[i]!=',';i++) tmon[tcnt++]=tinp[i];
- tmon[tcnt]='\0';
- int mon=atoi(tmon);
- char tc[15];tcnt=0;
- for(i++;tinp[i]!=',';i++) tc[tcnt++]=tinp[i];
- tc[tcnt]='\0';t1=atof(tc);tcnt=0;
- for(i++;tinp[i]!=',';i++) tc[tcnt++]=tinp[i];
- tc[tcnt]='\0';t2=atof(tc);tcnt=0;
- for(i++;tinp[i]!=',';i++) tc[tcnt++]=tinp[i];
- tc[tcnt]='\0';t3=atof(tc);tcnt=0;
- for(i++;tinp[i]!=',';i++) tc[tcnt++]=tinp[i];
- tc[tcnt]='\0';t4=atof(tc);tcnt=0;
- for(i++;tinp[i]!=')';i++) tc[tcnt++]=tinp[i];
- tc[tcnt]='\0';t5=atof(tc);tcnt=0;
- strcpy(sal[salcnt].id,tid);
- strcpy(sal[salcnt].name,tna);
- sal[salcnt].collegeid=tcd;
- sal[salcnt].jobid=tjd;
- sal[salcnt].month=mon;
- sal[salcnt].basicsal=t1;
- sal[salcnt].addsal=t2;
- sal[salcnt].subsal=t3;
- sal[salcnt].theosal=t4;
- sal[salcnt].truesal=t5;
- salcnt++;
- }
- fclose(f1);
- }
其核心爲檢測逗號所在位置,並以逗號爲分割符,進行數據類型的轉換操作,最後複製到相應的變量中。
3.0.1 教師信息結構體 及 學院職稱映射數組
- struct Teacher{
- char id[11];
- char name[31];
- int collegeid;
- int jobid;
- }ter[NTer],tmpt; // use tmp to change
- int tercnt=0; // count the number of all teacher
- char col_sec[Ncol+1][31]={ "","Information","Engineer","Theory","Arts","Biology"}; // college nubmer to name
- char job_sec[Njob+1][31]={ "","Professor","Ass-Professor","Teacher","Tutor"}; // job nubmer to name
該段特殊採用數字映射字符串的方法,將學院職稱皆量化爲int,方便後續的操作。
3.0.2 薪資信息結構體
- struct Salary{
- char id[11];
- char name[31];
- int collegeid;
- int jobid;
- int month;
- double basicsal, addsal, subsal, theosal, truesal;
- }sal[Nsal],tmps;
- int salcnt=0; // count the number of all records
basicsal—基本工資,addsal—業績津貼,subsal—扣除費用,theosal—應發工資,truesal—實發工資。
3.0.3 學院平均值統計結構體 與 職稱平均值統計結構體
- struct College
- {
- int collegeid;
- double thavg,travg;
- }col[6],tmpc;
- struct Job
- {
- int jobid;
- double thavg,travg;
- }job[6],tmpj;
進行統計分析時使用。
3.1 Operation Functions 概述
該部分代碼實現了程序與外接交互的全部功能。負責讀取用戶的操作輸入,並與用戶進行交互。 該部分具體內容將在【四】中進行演示並介紹。
3.2 Check Functions 概述
該部分代碼對實現程序魯棒性檢測及排除起到了重要作用,下面將依次介紹其實現方案。
3.2.1 checkten()--對輸入的數字範圍進行檢測
(應用於用戶菜單選擇等)
- int checkten(int L,int R)
- {
- char opeinp[20];
- scanf("%s",opeinp);
- while ((atoi(opeinp)) == 0 or (atoi(opeinp)) > R or (atoi(opeinp)) < L )
- {
- printf("your input is invalid,please input again(just number from %d to %d):____\b\b\b",L,R);
- scanf("%s",opeinp);
- }
- return atoi(opeinp);
- }
通過atoi函數實現輸入字符串向int類型的轉換工作,並判斷其大小範圍是否在給定的[L,R]區間內,如果非數字,或非所需區間,則給出提示並反覆要求用戶輸入,直至用戶輸入合法,返回該數值。
3.2.2 checkdb()--檢測用戶輸入是否爲實數
(用於工資輸入的魯棒性檢查)
- double checkdb()
- {
- char opeinp[20];
- scanf("%s",opeinp);
- while ((atof(opeinp)) == 0)
- {
- printf("your input is invalid,please input again(just a real number):________\b\b\b\b\b\b\b\b");
- scanf("%s",opeinp);
- }
- return atof(opeinp);
- }
通過atof函數,將輸入字符串轉換爲double類型,如若轉換失敗(atof返回值爲0),則給出提示並反覆要求用戶輸入,直至用戶輸入合法,返回該數值。
3.2.3 checkid()--檢測用戶輸入id是否合法且合規
(用於id輸入的魯棒性檢查)
- void checkid(char * inpid, int judgeexist)
- { // if judgeexist == 1 , until input exist then return;
- // if judgeexist == 2 , until input not exist return;
- while(scanf("%s",inpid))
- {
- if(inpid[10] != '\0')
- { // check 10 digit to stop
- printf("your ID format is invalid,please make sure it is consist of 10 nubmers,ID:__________\b\b\b\b\b\b\b\b\b\b");
- continue;
- }
- int i;
- for(i = 0; i < 10; i++)
- {
- if(inpid[i] > '9' or inpid[0] < '0')
- {
- printf("your ID format is invalid,please make sure it is consist of 10 nubmers,ID:__________\b\b\b\b\b\b\b\b\b\b");
- break;
- }
- }
- if(i==10)
- { // format valid
- // to check existence
- int f=0; // 0 not exist; 1 exist
- for(i = 0;i < tercnt; i++)
- {
- if(strcmp(ter[i].id, inpid) == 0)
- {
- f=1;
- //printf("your ID input is existed, please make sure it's new one's id,ID:__________\b\b\b\b\b\b\b\b\b\b");
- break;
- }
- }
- if(judgeexist==1)
- {
- if(f==0)
- {
- printf("your ID input is not existed, please make sure it's above one's id,ID:__________\b\b\b\b\b\b\b\b\b\b");
- continue;
- }
- if(f==1)
- {
- return ;
- }
- }
- if(judgeexist==2)
- {
- if(f==1)
- {
- printf("your ID input is existed, please make sure it's new one's id,ID:__________\b\b\b\b\b\b\b\b\b\b");
- continue;
- }
- if(f==0)
- {
- return ;
- }
- }
- }
- }
- return ; // no use,just done for warning
- }
該函數6-10行判斷輸入是否爲10位,10位判斷合法後,進入每位是否爲數字的判斷,再次合法後,纔將進入可選mode的判斷。
該函數我設置爲可選mode,如果judgeexist == 1 ,則直到所輸入id存在於系統中才會返回,如果judgeexist == 1 ,則直到所輸入id是不事先存在的纔會返回。該實現有利於後續的實際操作
3.2.4 checkname()--檢測用戶名字是否存在
(用於檢索名字時的魯棒性檢測)
- int checkname(char * inpname)
- {
- int t=0;
- rewind(stdin);
- while(1)
- {
- gets(inpname);
- rewind(stdin);
- int i;
- for(i=0;i<tercnt;i++)
- {
- if(strcmp(ter[i].name,inpname) == 0)
- {
- return 1;
- }
- }
- printf("your name input is not existed, please make sure it's above one's name,NAME:__________\b\b\b\b\b\b\b\b\b\b");
- t++;
- if(t==5) break;
- }
- return 0;
- }
該函數將遍歷整個ter結構體內的所有name,直至有匹配才返回,如若一直未匹配,則證明不存在該名字,故提示用戶重新輸入。
3.2.5 checkmon()--用以檢測所要查詢的月份是否存在salary信息中
- int checkmon(int * mon,char * id)
- {
- int t=1; // tried too many times
- while(1)
- {
- t++;
- if(searchsalid(id,*mon)!=-1) return 1;
- printf("%d month do not have salary information\n",*mon);
- printf("please resure and then INPUT:_____\b\b\b");
- *mon=checkten(1,12);
- if(t==5) break;
- }
- return 0;
- }
通過調用searchsalid 並進行匹配實現(後續將介紹該函數)
3.3 Storage Functions 概述
這些函數實現了數據的錄入,修改,刪除,以及本地化SQL存儲等功能。
接下來將一一介紹。
3.3.1 inpter() inpsal()--ter輸入函數 與 sal輸入函數
- void inpter(int tid,int notfirst)
- {
- printf("Input the Teacher's NAME:______________\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
- rewind(stdin);
- gets(ter[tid].name); // to ignored the sperate by space
- printf("Input the college index of the teacher:\n1.Information,2.Engineer,3.Theory,4.Art,5.Biology. INPUT:___\b\b");
- ter[tid].collegeid=checkten(1,5);
- printf("Input the job index of the teacher:\n1.Professor,2.Ass-Professor,3.Teacher,4.Tutor. INPUT:___\b\b");
- ter[tid].jobid=checkten(1,4);
- struct Salary * sa;
- for(sa=sal;sa<sal+salcnt;sa++)
- {
- if(strcmp(sa->id,ter[tid].id)==0)
- {
- strcpy(sa->name,ter[tid].name);
- sa->jobid = ter[tid].jobid;
- sa->collegeid = ter[tid].collegeid;
- }
- }
- if(notfirst == 0) tercnt++;
- showter(3,ter[tid].id);
- if(notfirst == 0) tercnt--;
- printf("BELOW INFORMATION SAVED SUCCESSFULly \n");
- printf("PRESS ENTER TO CONTINUE.\n");getchar();getchar();
- }
- void inpsal(int sid,int notfirst)
- {
- printf("please input the month(nubmer from 1 to 12):______\b\b\b");
- sal[sid].month=checkten(1,12);
- printf("please input the basic salary:________\b\b\b\b\b\b\b\b");
- sal[sid].basicsal=checkdb();
- printf("please input the additional salary:________\b\b\b\b\b\b\b\b");
- sal[sid].addsal=checkdb();
- printf("please input the subtract salary:________\b\b\b\b\b\b\b\b");
- sal[sid].subsal=checkdb();
- sal[sid].theosal=sal[sid].basicsal+sal[sid].addsal;
- sal[sid].truesal=sal[sid].theosal-sal[sid].subsal;
- FreshSalName(sid);
- if(notfirst == 0) salcnt++;
- showsal(3,sal[sid].id,sal[sid].month);
- if(notfirst == 0) salcnt--;
- printf("BELOW INFORMATION SAVED SUCCESSFULlY \n");
- printf("PRESS ENTER TO CONTINUE.\n");getchar();getchar();
- }
該段代碼實現了排除魯棒性的輸入功能,並將教師信息中的姓名,學院,職稱等,(如果工資信息存在)更新給工資信息,且在輸入完成後,顯示最新信息,以便查驗。
3.3.2 delter() delsal()--刪除教師信息及薪資信息
- void deleteter(char * id)
- {
- // through swap and cnt-- to complete the delete operation
- swapter(&ter[tercnt-1],&ter[searchterid(id)]);
- tercnt--;
- }
- void deletesal(char * id)
- {
- int i;
- for(i=0;i<salcnt;i++)
- {
- if(strcmp(sal[i].id,id) == 0)
- {
- swapsal(&sal[i],&sal[salcnt-1]);
- salcnt--;
- }
- FreshSalName(i); // to check bug
- }
- }
該刪除方法爲:交換當前元素和最後一位元素的全部信息(通過自定swap實現,後續將進行介紹),並將總元素個數的統計變量減一,以此實現該刪除操作。
3.3.3 自定義簡便SQL操作
已在3.0.0中介紹完畢,此處不再贅述。
3.4 Algorithm Functions 概述
該部分實現該程序的主要算法功能,接下來將一一介紹各函數的實現方法。
3.4.1 showter()--可選mode的教師信息列表打印函數
- int showter(int mode,char * cc)
- { // mode == 1: show all information
- // mode == 2: show NAME(tc)'s infromation
- // mode == 3: show ID(tc)'s information
- int re=-1;
- struct Teacher * tc;
- printf("\n---------------------------Basic Information---------------------\n");
- printf("|ID\t\t|NAME\t\t|COLLEGE\t|JOB\t\t|\n");
- printf("-----------------------------------------------------------------\n");
- for(tc=ter;tc<ter+tercnt;tc++)
- {
- if(mode == 1) printf("|%s\t|%-15s|%-15s|%-15s|\n",tc->id,tc->name,col_sec[tc->collegeid],job_sec[tc->jobid]);
- if(mode == 2 && (strcmp(tc->name,cc) == 0) ) printf("|%s\t|%-15s|%-15s|%-15s|\n",tc->id,tc->name,col_sec[tc->collegeid],job_sec[tc->jobid]);
- if(mode == 3 && (strcmp(tc->id,cc) == 0) )
- {
- re=tc-ter; // return his cntid
- printf("|%s\t|%-15s|%-15s|%-15s|\n",tc->id,tc->name,col_sec[tc->collegeid],job_sec[tc->jobid]);
- }
- }
- printf("-----------------------------------------------------------------\n\n");
- return re;
- }
- void showsal(int mode,char * cc,int mon)
- { // mode == 1: show all information
- // mode == 2: show NAME(tc)'s infromation
- // mode == 3: show ID(tc)'s information
- // if mon == 0 then cout every information
- // if mon == 1~12 then just cout the month's information
- struct Salary * sa;
- printf("\n-------------------------------------------------Salary Information------------------------------------------------------\n");
- printf("|Month\t|ID\t\t|NAME\t\t|Basic($)\t|Allowance($)\t|Subtract($)\t|Theory($)\t|True($)\t|\n");
- printf("-------------------------------------------------------------------------------------------------------------------------\n");
- for(sa=sal;sa<sal+salcnt;sa++)
- {
- if(mode == 1 && (mon==0?1:sa->month==mon)) printf("|%d\t|%-15s|%-15s|%-15.2f|%-15.2f|%-15.2f|%-15.2f|%-15.2f|\n",sa->month,sa->id,
- sa->name,sa->basicsal,sa->addsal,sa->subsal,sa->theosal,sa->truesal);
- if(mode == 2 && (strcmp(sa->name,cc) == 0) && (mon==0?1:(sa->month==mon))) printf("|%d\t|%-15s|%-15s|%-15.2f|%-15.2f|%-15.2f|%-15.2f|%-15.2f|\n",sa->month,sa->id,
- sa->name,sa->basicsal,sa->addsal,sa->subsal,sa->theosal,sa->truesal);
- if(mode == 3 && (strcmp(sa->id,cc) == 0) && (mon==0?1:(sa->month==mon))) printf("|%d\t|%-15s|%-15s|%-15.2f|%-15.2f|%-15.2f|%-15.2f|%-15.2f|\n",sa->month,sa->id,
- sa->name,sa->basicsal,sa->addsal,sa->subsal,sa->theosal,sa->truesal);
- }
- printf("-------------------------------------------------------------------------------------------------------------------------\n\n");
- }
當mode爲1時,將展示所有教師的基本信息或薪水信息;當mode爲2時,將展示姓名爲所傳入參數的教師的基本信息或薪水信息;當mode爲3時,將展示ID爲所傳入參數的教師基本信息或薪水信息。
並且該函數利用了printf的格式化輸出功能,保證列表的整齊與美觀。
3.4.2 searchterid() searchsalid()--尋找所傳id對應的實際索引以便後續操作
- int searchterid(char * id)
- {
- int i;
- for(i=0; i < tercnt;i++)
- {
- if(strcmp(ter[i].id,id)== 0) return i;
- }
- return -1;
- }
- int searchsalid(char * id,int month)
- {
- int i;
- for(i=0;i<salcnt;i++)
- {
- if(strcmp(id,sal[i].id)==0 && sal[i].month == month) return i;
- }
- return -1;
- }
額外地,在考慮查詢月份需求的情況下,特假設month參數,以選擇特定的month進行輸出(給定 month和id時才能唯一的確定一條記錄)。
3.4.3 swapter()swapsal()swapcol()swapjob()--交換函數
全部內容交換函數,通過臨時變量交換法實現,此處不做贅述。
3.4.4 sortter_id() sortsal_id()--排序函數
以id爲序,對ter和sal的排序函數
- void sortter_id()
- { // make the ter list sorted by the id
- int i,j;
- for(i=0;i<tercnt;i++)
- for(j=tercnt-1;j>i;j--)
- if(strcmp(ter[j].id,ter[j-1].id)<0)
- swapter(&ter[j],&ter[j-1]);
- }
- void sortsal_id()
- { // make the sal list sorted by the 1:id 2:month
- int i,j;
- for(i=0;i<salcnt;i++)
- for(j=salcnt-1;j>i;j--)
- {
- if(strcmp(sal[j].id,sal[j-1].id)<0)
- swapsal(&sal[j],&sal[j-1]);
- if(strcmp(sal[j].id,sal[j-1].id) == 0)
- if(sal[j].month < sal[j-1].month)
- swapsal(&sal[j],&sal[j-1]);
- }
- }
由於禁止使用C++的algorithm庫及其sort函數,以及bool cmp() 排序規則,在此處我通過手寫的方式,實現了相應的功能。基於冒泡排序和前面的swap函數實現。
3.4.5 calcolavg_sort() caljobavg_sort()--實現統計分析排序功能的函數
- void calcolavg_sort(int mon)
- {
- int i,j;
- for(i=1;i<=Ncol;i++)
- {
- double thsum=0,trsum=0,cnt=0; // th->theory tr->true
- for(j=0;j<salcnt;j++)
- {
- if(sal[j].collegeid == i && sal[j].month == mon)
- {
- thsum += sal[j].theosal;
- trsum += sal[j].truesal;
- cnt++;
- }
- }
- col[i].collegeid = i;
- col[i].thavg= thsum?thsum/cnt:0;
- col[i].travg= trsum?trsum/cnt:0;
- }
- for(i=1;i<=Ncol;i++)
- for(j=Ncol;j>i;j--)
- {
- if(col[j].travg > col[j-1].travg)
- swapcol(&col[j],&col[j-1]);
- if(col[j].travg == col[j-1].travg)
- if(sal[j].collegeid < sal[j-1].collegeid)
- swapcol(&col[j],&col[j-1]);
- }
- }
- void caljobavg_sort(int mon)
- {
- int i,j;
- for(i=1;i<=Njob;i++)
- {
- double thsum=0,trsum=0,cnt=0; // th->theory tr->true
- for(j=0;j<salcnt;j++)
- {
- if(sal[j].jobid == i && sal[j].month == mon)
- {
- thsum += sal[j].theosal;
- trsum += sal[j].truesal;
- cnt++;
- }
- }
- job[i].jobid = i;
- job[i].thavg= thsum?thsum/cnt:0;
- job[i].travg= trsum?trsum/cnt:0;
- }
- for(i=1;i<=Njob;i++)
- for(j=Njob;j>i;j--)
- {
- if(job[j].thavg > job[j-1].thavg)
- swapjob(&job[j],&job[j-1]);
- if(job[j].travg == job[j-1].travg)
- if(sal[j].jobid < sal[j-1].jobid)
- swapjob(&job[j],&job[j-1]);
- }
- }
該段程序通過對所選職業或學院的相應工資信息進行累加,並存儲在job和college兩個結構體中,最後對這兩個結構體進行排序實現以上功能。
3.4.6 FreshSalName()--同步sal與ter之間的信息
由於涉及到對salary信息與teacher信息的同步更新,設定此函數
- void FreshSalName(int salind)
- {
- int i;
- for(i=0; i<tercnt;i++)
- {
- if(strcmp(ter[i].id,sal[salind].id) == 0)
- {
- strcpy(sal[salind].name,ter[i].name);
- sal[salind].collegeid=ter[i].collegeid;
- sal[salind].jobid=ter[i].jobid;
- break;
- }
- }
- }
通過比對id後,進行賦值,實現相應功能。
四、結果展示:
4.0開始界面
展示軟件信息及版本信息
進行321的倒計數後,進入程序的主界面
進入操作主菜單
魯棒性測試(只有輸入給定的操作數纔可繼續)
4.1進入錄入數據並保存功能
4.1.1教師信息錄入功能
(錄入學號姓名等,並以代號形式錄入學院,職稱信息)
最終將該信息以列表的形式打印出來
魯棒性測試4.1.1.1(教師id重複)
魯棒性測試4.1.1.2(教師id出現特殊字符,id過長,id過短)
魯棒性測試4.1.1.3(學院、職稱出現特殊字符,過長,過短)
4.1.2錄入教師工資信息
按照提示錄入相應信息後,將會輸出該調工資信息(人性化交互)
4.1.2.1 魯棒性測試(輸入不存在的id)
4.1.2.2 魯棒性測試(輸入不合法的月份信息)
4.1.2.3魯棒性測試(輸入不合法的錢數信息)
4.1.3 保存到本地功能
已成功存儲到結構體和本地的dat文件中。
4.2 進入修改數據並保存功能
4.2.1 根據工號修改某位教師的基本信息和工資信息
修改基礎信息:
修改工資信息:
4.2.1.1魯棒性測試(當月份信息不存在時)
4.2.1.2魯棒性測試(當多次輸入錯誤月份信息時)--跳出避免死循環
其他魯棒性繼承於前序操作,此處不再重複演示。
4.2.2 根據姓名修改某位教師的基本信息和工資信息
Case1:該姓名不重複
Case2:該姓名存在重複情況 – 選擇所需要選擇的id輸出即可
4.2.2.1魯棒性測試(所輸入的名字不存在) -- 提示重新輸入
4.2.3 刪除某一工號的教師的所有信息
將所有信息列出後,要求輸入所要刪除人的id
經過多次確認後方可刪除,刪除後輸出最新的全部信息表格
4.3查詢顯示功能
4.3.1展示所有教師的基本信息
4.3.2根據月份顯示所有教師當月工資信息(以工號爲序)
4.3.3根據工號查詢某位教師某一月份的基本信息和工資信息
4.3.4根據姓名查詢某位教師某一月份的基本信息和工資信息
Case1:無重複時
Case2:有重複時
4.3.4.1魯棒性測試(姓名不存在)
4.4統計分析
4.4.1根據月份,統計不同學院教師的平均應發工資和平均實發工資
4.4.2根據月份,統計不同職稱教師的平均應發工資和平均實發工資
4.5從本地導入數據(**創新點**)
可選從上次存儲數據中導入,或從演示樣例數據中進行導入
導入成功後,將顯示所導入的信息
4.6保存並退出功能
經過多次確認後方可退出
退出感謝信息
五、結束語:
以上爲該程序的全部實現方法及演示,代碼已傳至個人GitHub上,該項目地址爲:https://github.com/ShuoCHN/SalaryManageSystem。如有任何疑問或發現存在bug,請加私信與我進行討論,謝謝!