软件开发与创新课程设计作业1——员工信息管理系统的分析与二次开发
一、项目介绍
项目名称:员工管理系统
项目来源:员工管理系统
项目概要:该项目是用C语言实现的一个管理企业员工的系统,它主要实现的功能有:
- 输入某企业现有员工信息并保存在文件中
- 将所有员工信息或目标员工信息输出在界面中
- 删除离开企业的员工信息
- 添加新加入企业的员工信息并保存在文件中
- 找到目标员工,对其信息进行修改
二、源程序分析
首先给出项目源代码:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<conio.h> 5 struct employee 6 { 7 char no[40]; //职工号 8 char name[40]; //职工姓名 9 char sex[10]; //职工性别 10 int age; //职工年龄 11 struct employee *next;//下一结点指针 12 }; 13 typedef struct employee EMP; 14 EMP *h; 15 16 void load(); //读文件数据,并创建链表 17 void save(); //将链表中数据写到文件中 18 void add(EMP *p); //链表中增加职工结点 19 void del(char *s); //链表中删除指定姓名的职工结点 20 void update(); //链表中更新职工结点中的数据 21 void browse(); //在屏幕上显示链表中所有结点数据 22 void menu(); //显示系统主菜单 23 void delEmp(); //删除职工 24 void addEmp(); //增加职工 25 void main(void) 26 { 27 28 int op;//operation 29 h =(EMP *)malloc(sizeof(EMP)); //创建链表的头结点 30 h->next = NULL; //初始化链表头节点中下一节点指针为NULL 31 menu(); 32 scanf("%d",&op); 33 while(op!=0) 34 { 35 switch(op) 36 { 37 case 1: 38 load(); 39 break; 40 case 2: 41 save(); 42 break; 43 case 3: 44 addEmp(); 45 break; 46 case 4: 47 delEmp(); 48 break; 49 case 5: 50 update(); 51 break; 52 case 6: 53 browse(); 54 break; 55 } 56 menu(); 57 scanf("%d",&op); 58 } 59 } 60 61 //显示系统主菜单函数 62 void menu() 63 { 64 printf(" Main Menu\n"); 65 printf(" ********************** \n"); 66 printf(" * 0:quit 1 :load * \n"); 67 printf(" * 2:save 3 :add * \n"); 68 printf(" * 4:delete 5 :update * \n"); 69 printf(" * 6:browse 7 :default* \n"); 70 printf(" ********************** \n"); 71 } 72 73 //读文件数据,并创建链表函数 74 void load() 75 { 76 FILE *fp; 77 char ch; 78 79 fp = fopen("data.txt","r"); 80 if(fp==NULL) 81 { 82 printf("打开文件错误,按任意返回主菜单\n"); 83 getch();//程序停下来,等待输入 84 return; 85 } 86 while(!feof(fp)) 87 { 88 EMP *node =(EMP *)malloc(sizeof(EMP)); 89 node->next = NULL; 90 fscanf(fp,"%s%s%s%d",node->no,node->name,node->sex,&node->age);//从文件中读一行职工信息 91 add(node); // 调用函数增加链表结点 92 } 93 fclose(fp); 94 } 95 96 //链表中增加职工结点的函数 97 void add(EMP *p) 98 { 99 EMP *q; 100 q = h->next; //将q指针指向链表中第一个职工结点 101 if(q==NULL) //如果q指针为NULL,则表示当前链表为空 102 { 103 h->next = p; //p指针指向的结点为链表中第一个结点 104 p->next = NULL; 105 } 106 else 107 { 108 while(q->next!=NULL) //通过while循环找到链表中最后一个结点 109 { 110 q = q ->next; 111 } 112 q->next = p; //将q指针指向的最后一个结点的next指针指向新增结点 113 p->next =NULL; //现在p指针指向的是最后一个结点,因此将该节点的next指针设为NULL 114 } 115 116 } 117 118 //增加职工函数 119 void addEmp() 120 { 121 EMP *node; 122 node = (EMP *)malloc(sizeof(EMP)); 123 node->next = NULL; 124 printf("input the employee's no name sex age:\n"); 125 scanf("%s%s%s%d",node->no,node->name,node->sex,&node->age); 126 add(node); 127 128 } 129 130 //删除职工函数 131 void delEmp() 132 { 133 char name[40]; 134 printf("input del name:\n"); 135 scanf("%s",name); 136 del(name); 137 } 138 139 //在屏幕上显示链表中所有职工结点数据函数 140 void browse() 141 { 142 EMP *node; 143 node = h->next; //node指针指向链表第一个结点 144 printf("Employee no name age sex\n"); 145 while(node) //遍历链表 146 { 147 printf("%12s%6s%5d%4s\n",node->no,node->name,node->age,node->sex); 148 node = node->next; //node指针指向下一个职工结点 149 } 150 printf("\n"); 151 } 152 153 154 //将链表中数据写到文件中的函数 155 void save() 156 { 157 FILE *fp; 158 EMP *node; 159 fp = fopen("data.txt","w"); //以写的方式打开文件 160 node = h->next; //node指针指向链表第一个结点 161 while(node!=NULL) 162 { //"%s %s %s %d\n" 格式符之间有空格 163 fprintf(fp,"%s %s %s %d\n",node->no,node->name,node->sex,node->age); 164 node = node ->next; 165 } 166 fclose(fp); 167 } 168 169 //删除职工结点的函数 170 void del(char *s) 171 { 172 EMP *p,*q; 173 p = h->next;//链表中第一个节点 p->next=p->next->next; 174 q = p->next;//链表中第二个节点 175 if(strcmp(s,p->name)==0) 176 {h->next=p->next; 177 free(p); 178 return; 179 } 180 while(q!=NULL) 181 { 182 if(strcmp(s,q->name)==0) 183 { 184 p->next = q->next; 185 free(q); 186 break; 187 } 188 p = q; 189 q = q->next; 190 } 191 } 192 193 //更新职工结点的函数 194 void update() 195 { 196 char name[40]; 197 int flag = 0; 198 EMP *p; 199 printf("input the name:\n"); 200 scanf("%s",name); 201 p = h->next; 202 while(p!=NULL) 203 { 204 if(strcmp(name,p->name)==0) 205 { 206 printf("input the age:\n"); 207 scanf("%d",&p->age); 208 printf("input the sex:\n"); 209 getchar(); 210 scanf("%c",&p->sex); 211 flag = 1; 212 break; 213 } 214 p=p->next; 215 216 } 217 if(flag==0) 218 { 219 printf("error username\n"); 220 } 221 }
仅从源代码的关键操作代码中观察,我们不难发现,该系统选择的是使用链表结构完成对员工信息的操作。
- 链表相比于常规使用数组存储有着其特有的优越性,即该系统的容量不是固定的,具有可扩展性,与此同时它并不会产生内存的浪费。
- 但是因为用户可能经常会需要对信息进行查询,而链表不能够像索引一样随机访问任何元素,所以访问节点需要花费更多的时间。
下面运行一下程序:
以下是程序的主界面,

大概能从主菜单中明白输入各个数字能够进行的操作是什么,但笔者个人感觉可能添加一个输入提示会比较好,因为系统是给用户用的,而他们可能并不像开发者一样知道要输入对应数字之后才能够实现这些操作...(有抬杠的成分)
因为目标文件为空,所以从逻辑上来说,我们应该先进行添加操作:

这是笔者在输入个人信息之后得到的结果。我们发现在输入个人信息完成之后回车,程序直接再进行了一次主菜单的打印,这里出现第二个菜单感觉上有些许突兀了...用户可能会感觉莫名奇妙,并且不知道自己输入的信息是否添加成功,此处把这个菜单的打印换成提示信息添加成功是否会更加合适。
输入了信息之后,欸,心满意足,但一看,emmmm,怎么是200岁嘞?_?哦手贱多输入了一个0,可是这真的是能够输入的吗?
虽然程序包含了信息的修改功能,但是笔者更偏向于修改功能若是还要承担对这样“一眼错”数据的修改,多少是有点承受了它不该承受的工作量-.-。因此对于一些信息的输入,还是有必要添加一些限制语句,如果输入太浮夸的话需要有一个报错。
既然没有报错模块,那么我们就先更新一下数据好了:

诶嘿,打错名字了,不过报错了哎- -
那再输入一遍正确名字:

感觉格式好像跟我们预计的并不一致...主菜单竟然输入了两遍?
于是想到输入6在界面中显示链表中所有员工节点数据函数,看看信息是否已经完成修改:

emmmmm,性别的信息有点莫名其妙。
赶紧找出这一部分代码:
1 void update() 2 { 3 char name[40]; 4 int flag = 0; 5 EMP *p; 6 printf("input the name:\n"); 7 scanf("%s",name); 8 p = h->next; 9 while(p!=NULL) 10 { 11 if(strcmp(name,p->name)==0) 12 { 13 printf("input the age:\n"); 14 scanf("%d",&p->age); 15 printf("input the sex:\n"); 16 getchar(); 17 scanf("%c",&p->sex); 18 flag = 1; 19 break; 20 } 21 p=p->next; 22 23 } 24 if(flag==0) 25 { 26 printf("error username\n"); 27 } 28 }
ok,直接逮捕犯罪语句。

结构体中的定义sex是一个字符数组,格式输出应当为"%s"而非"%c"。
最后测试一下删除功能:

欸又输错了,但是没有报错。
再正确输入:

这里是没有问题的~
以上笔者所测试的外加一个退出判定,其实就已经是用户所能够使用到的所有功能了,其余的像load()函数应该在主函数中被调用一遍,save()函数应该放在add()函数中,即添加过后就保存,数字7对应的操作也无实际意义。以上都是可以改进的点。
三、修改方案
其实在源程序分析的过程中,笔者已经提出了一些意见,以及修改的方案,但为了方便读者阅读以及后续工作的开展,在这一部分将系统地提出一些改进方案。
- 优化主菜单界面。
- 解决主菜单多次打印问题。
- 给需要的信息(年龄、性别等)添加限制语句,并完善其报错功能。
- 给需要的模块(删除模块)进行输入错误判定。
- 修复修改模块输入格式不对造成的bug
- 将更新模块和删除模块中用姓名查找改为用编号查找,避免重名造成的问题
- 添加查询模块
四、修改后的代码及其运行结果
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<conio.h> struct employee { char no[40]; //职工号 char name[40]; //职工姓名 char sex[10]; //职工性别 int age; //职工年龄 struct employee *next;//下一结点指针 }; typedef struct employee EMP; EMP *h; void load(); //读文件数据,并创建链表 void save(); //将链表中数据写到文件中 void add(EMP *p); //链表中增加职工结点 void del(char *s); //链表中删除指定编号的职工结点 void update(); //链表中更新职工结点中的数据 void browse(); //在屏幕上显示链表中所有结点数据 void menu(); //显示系统主菜单 void delEmp(); //删除职工 void addEmp(); //增加职工 void select(); //查询职工信息 int main(void) { int op;//operation h =(EMP *)malloc(sizeof(EMP)); //创建链表的头结点 h->next = NULL; //初始化链表头节点中下一节点指针为NULL menu(); //load(); scanf("%d",&op); while(op!=0) { switch(op) { case 1: addEmp(); break; case 2: delEmp(); break; case 3: update(); break; case 4: browse(); break; case 5: select(); break; } printf("\n"); scanf("%d",&op); } } //显示系统主菜单函数 void menu() { printf(" Main Menu\n"); printf(" *************************\n"); printf(" * 0:quit 1:add * \n"); printf(" * 2:delete 3:update * \n"); printf(" * 4:browse 5:select * \n"); printf(" *************************\n"); printf("请输入你想要进行的操作前的数字:\n"); } //读文件数据,并创建链表函数 void load() { FILE *fp; char ch; fp = fopen("data.txt","r"); if(fp==NULL) { printf("打开文件错误,按任意返回主菜单\n"); getch();//程序停下来,等待输入 return; } while(!feof(fp)) { EMP *node =(EMP *)malloc(sizeof(EMP)); node->next = NULL; fscanf(fp,"%s%s%s%d",node->no,node->name,node->sex,&node->age);//从文件中读一行职工信息 add(node); // 调用函数增加链表结点 } fclose(fp); } //链表中增加职工结点的函数 void add(EMP *p) { EMP *q; q = h->next; //将q指针指向链表中第一个职工结点 if(q==NULL) //如果q指针为NULL,则表示当前链表为空 { h->next = p; //p指针指向的结点为链表中第一个结点 p->next = NULL; } else { while(q->next!=NULL) //通过while循环找到链表中最后一个结点 { q = q ->next; } q->next = p; //将q指针指向的最后一个结点的next指针指向新增结点 p->next =NULL; //现在p指针指向的是最后一个结点,因此将该节点的next指针设为NULL } } //增加职工函数 void addEmp() { EMP *node; node = (EMP *)malloc(sizeof(EMP)); node->next = NULL; printf("input the employee's no name sex age:\n"); scanf("%s%s%s%d",node->no,node->name,node->sex,&node->age); while(node->age>100||node->age<=0) { printf("年龄输入有误!请仔细检查后重新输入!\n"); scanf("%s%s%s%d",node->no,node->name,node->sex,&node->age); } add(node); //save(); printf("信息添加完成!请继续您的操作!\n"); } //删除职工函数 void delEmp() { char no[40]; int flag = 0; EMP *p; printf("input del no:\n"); scanf("%s",no); p=h->next; while(p!=NULL) { if(strcmp(no,p->no)==0) { flag = 1; break; } p=p->next; } if(flag==0) { printf("error userno\n"); return; } del(no); printf("信息已删除!请继续您的操作!\n"); } //在屏幕上显示链表中所有职工结点数据函数 void browse() { EMP *node; node = h->next; //node指针指向链表第一个结点 //printf("Employee no name age sex\n"); while(node) //遍历链表 { printf("%12s %6s%5d%4s\n",node->no,node->name,node->age,node->sex); node = node->next; //node指针指向下一个职工结点 } printf("\n"); } //将链表中数据写到文件中的函数 void save() { FILE *fp; EMP *node; fp = fopen("data.txt","w"); //以写的方式打开文件 node = h->next; //node指针指向链表第一个结点 while(node!=NULL) { //"%s %s %s %d\n" 格式符之间有空格 fprintf(fp,"%s %s %s %d\n",node->no,node->name,node->sex,node->age); node = node ->next; } fclose(fp); } //删除职工结点的函数 void del(char *s) { EMP *p,*q; p = h->next;//链表中第一个节点 p->next=p->next->next; q = p->next;//链表中第二个节点 if(strcmp(s,p->no)==0) {h->next=p->next; free(p); return; } while(q!=NULL) { if(strcmp(s,q->no)==0) { p->next = q->next; free(q); break; } p = q; q = q->next; } } //更新职工结点的函数 void update() { char no[40]; int flag = 0; EMP *p; printf("input the no:\n"); scanf("%s",no); p = h->next; while(p!=NULL) { if(strcmp(no,p->no)==0) { printf("input the age:\n"); scanf("%d",&p->age); printf("input the sex:\n"); //getchar(); scanf("%s",&p->sex); flag = 1; break; } p=p->next; } if(flag==0) { printf("error userno\n"); return; } printf("信息添加完成!请继续您的操作!\n"); } void select() { char no[40]; int flag = 0; EMP *p; printf("input the no:\n"); scanf("%s",no); p = h->next; while(p!=NULL) { if(strcmp(no,p->no)==0) { printf("no:%s name:%s sex:%s age:%d\n",p->no,p->name,p->sex,p->age); flag = 1; break; } p=p->next; } if(flag==0) { printf("error userno\n"); return; } printf("信息查询完成!请继续您的操作!\n"); }
- 运行程序后的主界面:

2.添加信息功能:

添加信息的错误输入:

此时链表中的数据:

3.删除信息功能

错误输入:

此时链表中的数据:

4.更新信息功能:

错误输入:

此时链表中数据:

5.查询功能:

错误输入:

6.退出

最后给出大部分调试过程:

以上便是本文所完成的所有工作。
五、实验总结和感悟
本文介绍的员工信息管理系统其实是具有十分良好的扩展性的,这点在原博主的行文中也提及过,而笔者仅仅对于该系统现存的一些疏漏、不合理之处进行了优化,并新添了查询功能。根据目标用户的需求,其实能够设计出更多的功能,但受限于笔者的时间、精力以及能力,对于该系统的完善只能止步于此。
那么笔者从中得到了什么呢?稍微列举一下吧,主要是给自己看:
1.最好把功能分开用自定义函数完成...方便调试的时候发现错误。
2.复习了链表的遍历、链表的插入等链表相关内容。
3.完成一个项目时,输入错误或不合理的报错还是有必要的,这样能减少很多麻烦。
4.对于C语言file类文件有了更加深入的理解。
5.在改进程序二次开发过程中,其实我们是既作为开发者,又作为用户来面对程序的,很大程度上规避了自己单纯作为开发者,一切以自己思想为中心构建功能的一些弱点,并在以后独立编程时希望更多的能够站在用户角度看问题。
本篇博客是笔者所作的第一篇博客,这样的创作方式也是十分奇妙的一种体验。也正因为如此,加之笔者只是一个再平凡不过的软件工程专业大学生,文章中必然存在纰漏,欢迎同学们批评指正!

浙公网安备 33010602011771号