C语言 人事管理系统练习
生活随笔
收集整理的這篇文章主要介紹了
C语言 人事管理系统练习
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C語言 人事管理系統練習
一、簡述
? ? 簡單的一個人事管理系統程序。使用鏈表進行數據的操作(增刪改查、排序),最后將數據保存在文件中。
? ? (例子中的數據錄入不嚴謹,結構體設計也是有待改進)
? ? 例子打包:鏈接: https://pan.baidu.com/s/1FZ_6qzejhmb9D0exqttB8w 提取碼: xra3?
二、效果
三、工程結構
四、源文件
head.h文件
#define FILENAME "employee.Liang" #define HEADER "工號--------姓名------年齡----性別------出生年月------地址------電話----Email\n" #define FORMATSTR " %4s\t%8s\t%4s\t%4s\t%4s\t\t%4s \t%4s \t%4s\n" #define FORMAT_PNT(p) (p)->num,(p)->name,(p)->age,(p)->sex,(p)->birth,(p)->addr,(p)->tel,(p)->emailtypedef struct employee //員工結構體 定義 {char num[4];// 工號char name[8];// 姓名char age[4];//年齡char sex[4];//性別char birth[4];//出生年月char addr[4];//地址char tel[4];//電話char email[4];//emailstruct employee *next; }DATA;void About();//關于 void MainMenu();//主菜單函數 void Add(); //添加函數 void AddTail(DATA * newDATA);//尾部添加函數 void AddHead(DATA * newDATA);//頭部添加函數 void Show();//顯示全部數據 void Save();//保存到文件 void Read();//讀取文件 void Clear();//清除文件內容 void MyFree();//釋放鏈表 void FindByNum();//根據工號查詢 void Find();//查詢子菜單 void Delete();//刪除子菜單 void DeleteByNum();//根據工號刪除 void Modify();//根據工號修改 void Sort();//排序 void SortByNum();//根據工號排序main.c文件
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "head.h"DATA* head = NULL;//全局結構體指針,作為鏈表的頭指針 int main() {system("color 1f");//設置背景顏色 int choice = 10;//記錄用戶選擇 ,初始化為10 Read();//加載文件數據 while(1)//主循環 {MainMenu();fflush(stdin);//清空輸入緩沖區,防止scanf獲取到上次殘留輸入 scanf("%1d",&choice);//指定接收長度(因為只提供0~8功能選項)switch(choice)//根據用戶選擇執行相應功能 {case 1: Add();//添加 break;case 2: Show();//展示 break;case 3: Find();//查找 break;case 4: Delete();//刪除 break;case 5: Modify();//修改 break;case 6: Sort();//排序 break;case 9: break;case 8: About();//關于 break;case 7: Clear();//清空文件數據 break;case 0: exit(0);//退出程序 break;default:break;}}return 0; }void MainMenu()//主菜單界面 {system("cls");//清屏 printf(" \t┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");printf(" \t┃**********************************************************┃\n");printf(" \t┃***┏━━━━━━━━━━━━━━━━━━━━━━━━┓***┃\n");printf(" \t┃***┃************************************************┃***┃\n");printf(" \t┃***┃*** ****┃***┃\n");printf(" \t┃***┃*** 歡迎使用職工信息管理系統 ****┃***┃\n");printf(" \t┃***┃*** ****┃***┃\n");printf(" \t┃***┃*** 【1】錄入 ****┃***┃\n");printf(" \t┃***┃*** 【2】瀏覽 ****┃***┃\n");printf(" \t┃***┃*** 【3】查詢 ****┃***┃\n");printf(" \t┃***┃*** 【4】刪除 ****┃***┃\n");printf(" \t┃***┃*** 【5】修改 ****┃***┃\n");printf(" \t┃***┃*** 【6】排序 ****┃***┃\n");printf(" \t┃***┃*** 【7】清空 ****┃***┃\n");printf(" \t┃***┃*** 【8】關于 ****┃***┃\n");printf(" \t┃***┃*** 【0】退出 ****┃***┃\n");printf(" \t┃***┃*** ****┃***┃\n");printf(" \t┃***┃************************************************┃***┃\n");printf(" \t┃***┗━━━━━━━━━━━━━━━━━━━━━━━━┛***┃\n");printf(" \t┃**********************************************************┃\n");printf(" \t┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");printf(" \t ****************【請輸入您的選擇(0~8)】:"); }void Add()//添加函數 {Read();//將文件數據讀取到全局鏈表DATA* p = NULL;//用來遍歷鏈表,看看新添加的是否已存在 char isContinue = 'n';//控制是否繼續添加 ,控制是否已經存在 char cAdd = 'T';//控制是尾部添加還是頭部添加 ,或者是返回菜單 system("cls");//清屏 printf("\t\t====添加子菜單=====\n"); printf("\t\t【1】尾部添加\t\n");printf("\t\t【2】頭部添加\t\n");printf("\t\t【0】返回主菜單\t\n");printf("\t\t請選擇【0~2】:");fflush(stdin);//清空緩沖區,防止接收到上次存留輸入 cAdd = getchar();//接收用戶選擇 if(cAdd == '0') return;//返回主菜單 DATA *newDATA = NULL;//聲明結構體指針,用來指向新的結構體 do{newDATA =(DATA*)malloc(sizeof(DATA));//申請空間 if(newDATA == NULL)//申請失敗 {printf("%d:malloc error!\n",__LINE__);//__LINE__是宏定義,表示當前行號 return;//返回 }memset(newDATA,0,sizeof(DATA));//將新申請的空間清零 newDATA->next = NULL;//將結構體指針域置空//接收用戶輸入,記得指定長度,并且接收前清空輸入緩沖區 ,防止接收到非法數據 printf("【請輸入員工的 工資號】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->num));//數據不為空 p = head;//p指向頭指針,用p遍歷鏈表,頭指針不動 while(p!=NULL)//節點不為空 {if(strncmp(p->num,newDATA->num,4)==0)//指定長度比較工號 ,如果相等 ,p指向的節點就是所查找的 {isContinue = 'e' ;//標志工號已經存在 break;//直接結束循環 }p = p->next;//不相等,指向下一個節點 }if(isContinue !='e')//工號沒有重復才可以繼續添加 {printf("【請輸入員工的 姓名】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%8s",&(newDATA->name));printf("【請輸入員工的 年齡】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->age));printf("【請輸入員工的 性別】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->sex));printf("【請輸入員工的 出生年月】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->birth));printf("【請輸入員工的 地址】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->addr));printf("【請輸入員工的 電話】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->tel));printf("【請輸入員工的 E-mail】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 scanf("%4s",&(newDATA->email));if(cAdd == '1')AddTail(newDATA);//尾部添加 elseAddHead(newDATA);//頭部添加 }else//工號重復 {printf("工號已存在!\n");free(newDATA);//釋放申請的空間 } printf("【是否繼續錄入(y/n)】:");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 isContinue=getchar();//接收用戶輸入 }while(isContinue=='y' || isContinue=='Y');Save(); //將數據保存到文件 }void AddTail(DATA * newDATA)//尾部添加 {if(head == NULL)//如果鏈表為空 {head = newDATA;//直接讓頭指針指向第一個數據 return;}//頭指針不為空 DATA *p = head;while(p->next !=NULL)//循環直到p指向最后一個 {p = p->next;//p指向下一個 }p->next = newDATA;//將新的數據添加到尾部 }void Show()//顯示全部 {system("cls"); //清屏 Read();//從文件中讀取數據到全局鏈表中 int count = 0;//記錄數據數目 if(head==NULL)//如果頭指針為空,說明為空數據 {printf("空數據!\n");system("pause");//暫停,等待 return; }//數據不為空 DATA* p = head;//用p來逐個指向數據節點,頭指針不動 printf(HEADER);//打印標題 while(p!=NULL)//如果當前節點不為空 {count++;//數目加1 printf(FORMATSTR,FORMAT_PNT(p));//打印格式化數據 p = p->next;//p指向下一個節點 }printf("【總共有 %d 條記錄】\n",count);//打印數據條數 system("pause");//暫停,等待}void Save()//保存到文件 {FILE* pf = fopen(FILENAME, "w");//以寫方式操作文件 if (!pf)//如果文件指針為空,即打開文件失敗 {puts("保存文件時失敗\n");return;//返回 }DATA* p = head;//頭指針不動,用p來遍歷鏈表 while (p!=NULL)//當前節點不為空,繼續循環 {//fwrite (const void* p, size_t a, size_t b, FILE* pf);從p地址讀取b次數據到pf指向文件,每次讀取a字節 ,,返回讀取成功到的次數fwrite(p, 1, sizeof(DATA)-sizeof(struct employee *), pf);//sizeof(DATA)-sizeof(struct employee *)只保存數據域,不保存結構體中的指針域 p = p->next;//p指向下一個節點 }fclose(pf);//關閉文件 //puts("保存成功!\n"); } void Read()//讀取數據文件 {MyFree();//釋放鏈表空間 FILE* pf = fopen(FILENAME, "r");//以讀打開文件方式 if(pf==NULL || fgetc(pf) == EOF )//如果文件打開失敗,或者文件為空{return;//返回 } int flag = 0;//循環控制,讀取文件數據成功 ,置1,循環繼續 rewind (pf);//將文件指針指向文件開頭,(因為使用這個函數:fgetc(pf),文件指針已經移動了,不再指向開頭) DATA *newDATA = NULL;//聲明一個結構體指針,用來指向新的結構體 do{flag = 0;newDATA =(DATA*)malloc(sizeof(DATA));//申請空間,用來存放新數據 if(newDATA == NULL)//申請空間失敗 {printf("%d:malloc error!\n",__LINE__);//__LINE__是宏定義,表示當前行號 break;//結束循環 }memset(newDATA,0,sizeof(DATA));//將申請到的空間清0 newDATA->next = NULL;//將新結構體指針域置空 //fread (void* p, size_t a, size_t b, FILE* pf);從pf指向文件中每次讀取a個字節,讀b次 放到p 指向空間 ,返回讀取成功到的次數//記得減去結構體里面的指針域空間 struct employee * 所占空間其實就是4字節(如果是32位地址線) if(fread(newDATA,1,sizeof(DATA)-sizeof(struct employee *), pf)== sizeof(DATA)-sizeof(struct employee *)){flag = 1;//置1,繼續循環 AddTail(newDATA);//將從文件中讀取到的數據尾部添加到全局鏈表 }}while(flag);//能讀取到數據,繼續向下讀取 free(newDATA);//因為無論文件有無數據,讀取到最后時,最后申請的空間肯定沒有用上,所以要釋放 //newDATA = NULL;//個人習慣,釋放后置空fclose(pf);//關閉文件 } void Clear()//清空文件數據 {char c='n';//確認是否清空數據 printf("【確認清空數據】(y/n):");fflush(stdin);//接收前清空緩沖區,防止接收到上次存留輸入 c=getchar();//接收用戶輸入 if(c=='y' || c=='Y'){FILE *fp = fopen(FILENAME,"w");//以寫方式打開文件,之前的內容會被清空 fclose(fp);//關閉文件 printf("【清空數據成功!】\n");MyFree();//釋放鏈表空間 system("pause");//暫停,等待 } }void MyFree()//釋放鏈表空間 {DATA *p = head,*p1 = NULL;while (p!=NULL)//p1,p指向 相鄰的兩個節點,將p1釋放,然后將p1,p同時移動 {p1 = p;p = p->next;free(p1);//釋放p1指向空間 }head = NULL;//因為head是全局變量,且還會用到 ,所以釋放指定空間后置空,防止誤操作; }void FindByNum()//按照工號查找 {char num[4] = {0};//聲明一個字符數組,并初始化 printf("請輸要查找的入員工工號:");fflush(stdin);//接收輸入前清空輸入緩沖區,防止接收到你上次存留數據 scanf("%4s",num);//指定長度接收字符 Read();//從文件中讀取數據到全局鏈表 if(head==NULL)//如果頭指針為空,即數據為空 {system("cls"); //清屏 printf("空數據\n");system("pause");return;//直接返回 }//數據不為空 DATA* p = head;//p指向頭指針,用p遍歷鏈表,頭指針不動 while(p!=NULL)//節點不為空 {if(strncmp(p->num,num,4)==0)//指定長度比較工號 ,如果相等 ,p指向的節點就是所查找的 {break;//直接結束循環 }p = p->next;//不相等,指向下一個節點 }if(p!=NULL)//如果p指向不為空,說明找到了(p遍歷鏈表,找不到的話會指向空) {system("cls");//清屏 printf(" \t===【已經找到工號為:%4s 的員工】===\n",p->num);printf(HEADER);//打印頭部 printf(FORMATSTR,FORMAT_PNT(p));//格式化輸出數據 }else//p為空,說明找不到 {printf("\t====查無此人====\n");}system("pause");//暫停一下 } void Find()//查找菜單 {char choice = 'n';//控制是否繼續查找 while(1){system("cls");//清屏 printf("\t\t====查詢子菜單=====\n"); printf("\t\t【1】按工號查詢\t\n");printf("\t\t【2】按姓名查詢\t\n");printf("\t\t【3】按郵箱查詢\t\n");printf("\t\t【0】返回主菜單\t\n");printf("\t\t請選擇【0~3】:");fflush(stdin);//清空緩沖區 choice = getchar();//獲取用戶選擇 switch(choice)//根據選擇執行相應的功能函數 ,這里只是實現了按工號查詢 {case '1':case '2':case '3': FindByNum();break;case '0':return;default :break;}} } void DeleteByNum()//按照工號刪除 { char num[4] = {0};//申明字符數組用來接收要刪除的工號 char choice = 'n';//用來確認是否刪除 printf("請輸入要刪除的員工工號:");fflush(stdin);//清空緩沖區 scanf("%4s",num);//接收指定長度的輸入 Read();//從文件從讀取數據到全局鏈表 if(head==NULL)//如果頭指針為空,即數據為空 {system("cls"); //清屏 printf("空數據\n");system("pause");return;//返回 }//數據不為空 DATA* p = head;DATA* p1 = head;while(p!=NULL)//p1指向p,p遍歷節點 {if(strncmp(p->num,num,4)==0)//指定長度比較,如果相等,說明找到要刪除的節點 ,p指向要刪除的節點 {break;//退出循環 }p1 = p; //p1跟p指向同一節點,下面一句p指向下一節點,變為p1指向p p = p->next;}if(p!=NULL)//p不為空,說明找到要刪除的節點 {printf("\t====已經找到學號為:%4s 員工====\n",p->num);printf(HEADER);//打印頭部 printf(FORMATSTR,FORMAT_PNT(p));//格式化打印 printf("\t\t****確認刪除?[y/n]:"); fflush(stdin);//清空輸入緩沖區 choice = getchar();//接收輸入 if(choice == 'y' || choice == 'Y'){if(p == head)//如果找到的節點是頭節點 {head = p->next;//第二個節點成為頭部節點 } else{p1->next = p->next;//p1指向p的下一節點,p指向找到的節點,p1指向找到節點的前一節點 }free(p);//釋放p指向空間 p = NULL;//釋放后置空,習慣 Save();//將數據保存到文件 printf("\t\t****刪除成功!\n");}}else//p為空 {printf("\t====查無此人====\n");}system("pause"); } void Delete()//刪除 {char choice = 'n';//接收用戶選擇 while(1){system("cls");//清屏 printf("\t\t====刪除子菜單=====\n"); printf("\t\t【1】按工號刪除\t\n");printf("\t\t【2】按姓名刪除\t\n");printf("\t\t【3】按郵箱刪除\t\n");printf("\t\t【0】返回主菜單\t\n");printf("\t\t請選擇【0~3】:");fflush(stdin);//清空緩沖區 choice = getchar();//接收用戶輸入 switch(choice){case '1':case '2':case '3': DeleteByNum();break;case '0':return;default :break;}} }void Modify()//按照工號修改 {char num[4] = {0};//工號 char tel[4] = {0};//電話 char addr[4] = {0};//地址 char email[4] = {0};//郵箱 printf("請輸入要修改的員工工號:");fflush(stdin);//清空緩沖區 scanf("%4s",num); Read();//從文件讀取數據到全局鏈表 if(head==NULL)//頭指針為空,即數據為空 {system("cls"); printf("空數據\n");system("pause");return;}//數據不為空 DATA* p = head;while(p!=NULL)//查找 {if(strncmp(p->num,num,4)==0)//找到 {break;//退出循環 }p = p->next;//指向下一節點 }if(p!=NULL)//p不為空,說明找到了 {printf("\t====已經找到學號為:%4s 員工====\n",p->num);printf(HEADER);//打印頭部 printf(FORMATSTR,FORMAT_PNT(p));//格式化輸出 printf("【將addr 改為:】");fflush(stdin);//清空輸入緩沖區scanf("%4s",addr);strncpy(p->addr,addr,4);//字符串指定長度復制,給節點更新數據 printf("【將 tel 改為:】");fflush(stdin);//清空輸入緩沖區scanf("%4s",tel);strncpy(p->tel,tel,4);//字符串指定長度復制,給節點更新數據 printf("【將email改為:】");fflush(stdin);//清空輸入緩沖區scanf("%4s",email);strncpy(p->email,email,4);//字符串指定長度復制,給節點更新數據 Save();//將鏈表數據保存到文件 printf("\t\t==修改成功!\n");}else//p為空,找不到數據 {printf("\t====查無此人====\n");}system("pause"); }void SortByNum()//按工號排序 {if(head==NULL)//如果頭指針為空(數據為空) {system("cls"); //清屏 printf("空數據\n");system("pause");return;//返回 }//數據不為空 DATA* p = head;//p指向頭部 DATA* q = NULL;DATA* m = NULL;//從小到大排序,每比較一次,找到比較中的最小值。比如q指向開始比較位置 ,m,q進行比較,m指向較小的節點;//如果m不是同q指向同一節點了,m,q節點交換 while (p->next!=NULL)//循環比較,p遍歷鏈表 ,m,q進行比較,m指向較小的節點 {m = p;q = m->next;//m,q指向相鄰兩個節點,m在前,q在后 while (q!=NULL)//q從p指向節點的下一節點開始遍歷比較,將最小值放到p指向節點 {if (strncmp(m->num,q->num,4)>0)//m節點的大于q節點的 m = q;//m指向較小的 q = q->next;//q指向下一節點 }if (m != p)//m不同p指向,交換m,p節點值,使p指向本輪比較中的最小值 {DATA t = *p;*p = *m;p->next = t.next;t.next = m->next; *m = t;}p = p->next;//p指向下一節點 if(p==NULL) break;//p指向空,鏈表比較結束,退出循環 }p = head;//p重新指向鏈表頭部 printf(HEADER);//打印頭部 while(p!=NULL)//循環遍歷 {printf(FORMATSTR,FORMAT_PNT(p));//格式化輸出 p = p->next;//p指向下一節點 }system("pause");//暫停 }void Sort()//排序 {char choice = 'n';//功能選擇 while(1){system("cls");printf("\t\t====排序子菜單=====\n"); printf("\t\t【1】按工號排序\t\n");printf("\t\t【2】按姓名排序\t\n");printf("\t\t【3】按郵箱排序\t\n");printf("\t\t【0】返回主菜單\t\n");printf("\t\t請選擇【0~3】:");fflush(stdin);//清空緩沖區 choice = getchar();//接收用戶選擇 switch(choice){case '1':case '2':case '3': SortByNum();break;case '0':return;default :break;}} }void AddHead(DATA * newDATA)//頭部添加 {newDATA->next = head;//新節點指向頭節點 head = newDATA; //新節點成為頭節點 } void About() {system("cls");//清屏 printf("\t\t\t\t作者:Genven_Liang\n");printf("\t\t\t\t版本:v1.2\n");printf("\t\t\t\t日期:2018.06.25(改進)\n");printf("\t\t\t\t郵箱:\n");printf("\t\t\t\t自學路上共勉之!\n"); system("pause");//程序暫停,等待按鍵按下 }?
五、總結
?
1、鏈表操作需要注意空間的釋放,防止內存泄漏。不再使用的空間需要進行及時的釋放。
2、數據輸入合法性檢測有待增強。注意結構體個成員的存儲長度,程序中沒有進行嚴謹的設計,數據輸入也是隨便的輸入
總結
以上是生活随笔為你收集整理的C语言 人事管理系统练习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网页设计师自我修养资源指南
- 下一篇: python调用c++动态库