数据结构课程设计-班级考勤管理系统
写这个博客,主要是为了总结一下,之前写过的东西,方便以后回来看看,写得不好,勿喷!!!
题目描述:
设计一个对本班所有人员进行考勤的管理系统,要求系统管理员(班长、学委或纪律委员)可以查询、添加、删除、修改和统计考勤信息,每条考勤信息至少包括:编号,学号、姓名,日期,地点,当天第几节课,性质(只记录迟到,缺席和请假三种非出席情况)等,非管理员(本班所有人员)可以查询和统计本班考勤信息。查询和统计可以按姓名、日期、性质等分类查询,查询结果按一定顺序排序后显示(利用第7章相关排序算法)。
基本功能:
程序中的相关人员列表、考勤情况等都必须采用所学过的一种数据结构(链表、栈、队列、树等)存储,不能全部只使用数组。所有功能的操作是对数据结构(数组或链表等)中的数据进行操作。
每一个功能模块需要划分多个子模块,使用各自不同的结构体来储存不同的信息,实现信息的添加、删除、查询、修改和统计等操作。其他信息根据功能需求可以自行设计。
(1) 有良好的输出界面。
(2) 系统管理员的登录功能,非管理员(班级所有成员)的注册登录功能。
(3) 管理员和非管理员必须为本班人员(与自己班级人员名单为准)。
(4) 管理员对信息的添加、修改、删除、查询和统计(按不同情况查询或统计,结果按顺序排序显示,如统计某人所有的缺席或迟到情况、按日期统计某天所有缺席或迟到的人员(结果按学号显示)、显示所有缺席或迟到人员(结果按次数从多到少显示等等)。
(5) 非管理人员只有查询和统计功能。
(6) 系统可以查看全班所有人员名单,且答辩时系统至少已经录入10条以上迟到、缺席和请假信息。
扩展功能要求(不限以下3条):
(1) 在考勤信息中增加确认功能,班级成员可以对考勤情况进行确认。
(2) 运行文件对各种信息进行合理的保存和读取。
(3) 对考勤系统进行功能扩展,使其查询、统计等功能更合理、更方便、更具使用性。
我还是按照我之前的套路,一步一步地分析题目的需求,方便之后的代码编写。
开发平台:Dev-C++ 5.11
步骤一:分析功能需求
班级考勤管理系统,可以记录本班同学的考勤信息,实现了管理员登录功能和非管理员登录和注册功能,非本班人员不能登录和注册。
管理员登录成功,能够实现的功能有:1、查看个人信息2、修改密码3、确认考勤信息4、浏览所有学生的信息5、浏览所有考勤信息6、添加考勤记录7、删除考勤记录8、修改考勤记录9、查询考勤记录10、统计考勤信息11、重置其它学生密码。
非管理员登录成功,能够实现的功能有:1、查个人信息2、修改密码3、确认考勤信息4、浏览所有考勤信息5、查询考勤信息6、统计考勤信息。
注册功能只限非管理员和本班同学注册,管理员不用注册。
步骤二:分析业务逻辑
设计整个系统的功能逻辑,大概画出整体的功能流程图,方便代码编写。
分析完业务逻辑之后,我们还需要考虑三个问题:1、数据的保存2、数据的展示3、用户的交互。
数据保存:
从题目来看数据应保存在文件中,我选择的是保存在txt文件中。
数据展示:
数据的可视化我是直接在控制台中进行输出。
用户交互:
用户的输入输出都是通过控制台,进行交互的。
步骤三:编写代码
定义数据结构:
//结构体 学生的账号
struct ID
{
int role; //身份标志,0为非管理员,1为管理员
char id [15]; //账号
char name [10]; //姓名
char pwd [15]; //密码
int late; //迟到次数
int absent; //缺席次数
int vacation; //请假次数
int amount; //缺勤次数
} All_ID[55]; //定义55个账号管理
//结构体 考勤信息
typedef struct node * pointer; //结点指针类型
struct node //结点结构
{
int number; //编号
char student_ID [15]; //学号
char name [10]; //名字
char date [10]; //日期
char address [10]; //地址
char course [10]; //课程
char reason [10]; //性质
char check [10]; //确认情况
pointer next; //指针域
};
typedef pointer lklist; //单链表类型,即头指针类型
准备数据,(在工程目录下新建两个txt文件,用于存放数据。一个是ID.txt,用于存放所有同学的基本信息。另一个是考勤.txt,用于存放所有的考勤信息。)
注意:
每行数据用英文格式的空格隔开。
接下来,编写各功能函数~~~
1、void readFile_ID(struct ID All_ID[]):该函数用于读取所有帐号的信息,在程序开始时,将所有的帐号信息导入到程序中,用于后续的操作。
//读账号文件函数
void readFile_ID(struct ID All_ID[])
{
int i=0;
FILE *fp;
fp=fopen("ID.txt","r");
while(!feof(fp))
{
fscanf(fp,"%d %s %s %s %d %d %d %d\n",&All_ID[i].role,All_ID[i].id,All_ID[i].name,All_ID[i].pwd,&All_ID[i].late,&All_ID[i].absent,&All_ID[i].vacation,&All_ID[i].amount);
i++;
flag++; //用于记录有多少个账号
}
fclose(fp); //关闭文件
}
2、void writeFile_ID(struct ID All_ID[],int i):该函数用于存储所有帐号的信息,在程序运行过程中,若修改了帐号信息或考勤信息被修改后,将重新保存好信息。
//写账号文件函数
void writeFile_ID(struct ID All_ID[],int i)
{
FILE *fp;
int j;
fp=fopen("ID.txt","w");
for(j=0;j<i;j++)
{
fprintf(fp,"%d %s %s %s %d %d %d %d\n", All_ID[j].role,All_ID[j].id,All_ID[j].name,All_ID[j].pwd,All_ID[j].late,All_ID[j].absent,All_ID[j].vacation,All_ID[j].amount );
}
fclose(fp); //关闭文件
}
3、void browse(struct ID All_ID[],int i):该函数用于浏览所有帐号信息,主要用于管理员查看所有人的信息,方便管理和统计。
//浏览所有账号函数
void browse(struct ID All_ID[],int i)
{
int j=0;
printf("_________________________________________________________________________________________\n");
printf(" 账号 姓名 密码 迟到/次 缺席/次 请假/次 缺勤/次\n");
for( j=0 ; j<i ; j++ )
{
printf(" %-16s %-11s %-13s %-10d %-9d %-10d %-11d\n", All_ID[j].id,All_ID[j].name,All_ID[j].pwd,All_ID[j].late,All_ID[j].absent,All_ID[j].vacation,All_ID[j].amount );
}
printf("_________________________________________________________________________________________\n");
}
4、void init( struct ID All_ID[] , lklist head ):该函数用于初始化统计考勤信息,在程序开始时,将考勤信息按每个人分类别统计好,迟到有多少次,缺席有多少次,请假有多少次,每个人一共缺勤了多少次。用于后续的统计排序查看。
//初始化统计考勤信息
void init( struct ID All_ID[] , lklist head )
{
int i=0;
int a=0,b=0,c=0; // a用于记录迟到的人数,b用于记录缺席的人数,c用于记录请假的人数
pointer p;
for(i=0;i<flag;i++)
{
a=0;b=0;c=0;
p=head->next;
while(p!=NULL)
{
if( strcmp(All_ID[i].id,p->student_ID)==0 && strcmp(p->reason,"迟到")==0 )
{
a++; //如果存在迟到的学生,a就自增 1
}
else if( strcmp(All_ID[i].id,p->student_ID)==0 && strcmp(p->reason,"缺席")==0 )
{
b++; //如果存在缺席的学生,b就自增 1
}
else if( strcmp(All_ID[i].id,p->student_ID)==0 && strcmp(p->reason,"请假")==0 )
{
c++; //如果存在请假的学生,c就自增 1
}
p=p->next;
}
All_ID[i].late = a; //统计迟到人数
All_ID[i].absent = b; //统计缺席人数
All_ID[i].vacation = c; //统计请假人数
All_ID[i].amount = All_ID[i].late + All_ID[i].absent + All_ID[i].vacation; //统计缺勤人数
}
}
5、lklist creat():该函数用于读取所有的考勤信息,在程序开始时,将所有的考勤信息导入到程序中,以尾插法的方式生成一条不带头结点的链表,并返回头指针。
//创建单链表,导入学生考勤信息 尾插法
lklist creat()
{
pointer head,rear,s;
head = new node;
rear = head;
FILE *fp;
fp=fopen("考勤.txt","r");
while(!feof(fp))
{
s = new node;
fscanf(fp,"%d %s %s %s %s %s %s %s\n",&s->number,s->student_ID,s->name,s->date,s->address,s->course,s->reason,s->check);
rear->next = s;
rear = s;
count++; //用于记录有多少条考勤信息
}
rear->next = NULL;
fclose(fp); //关闭文件
return head;
}
6、void writeFile(lklist head):该函数用于存储所有的考勤信息,在程序运行过程中,若对考勤信息进行了改动,修改后,将所有考勤信息重新保存到文件中。
//将考勤信息写入文件
void writeFile(lklist head)
{
FILE *fp;
fp=fopen("考勤.txt","w");
int number=1;
pointer p;
p=head->next;
while(p!=NULL)
{
fprintf(fp,"%d %s %s %s %s %s %s %s\n", number,p->student_ID,p->name,p->date,p->address,p->course,p->reason,p->check );
p=p->next;
number++;
}
fclose(fp); //关闭文件
}
7、void cout(lklist head):该函数用于浏览所有的考勤信息,主要输出已经输入的考勤信息以及后期修改后的考勤信息。
//输出所有考勤信息
void cout(lklist head)
{
pointer p;
p=head->next;
printf("______________________________________________________________________________________________________________________\n");
printf(" 编号 学号 姓名 日期 地点 课程节数 缺勤性质 确认情况\n");
while(p!=NULL)
{
printf(" %-5d %-15s %-10s %-9s %-12s %-13s %-9s %-10s\n",p->number,p->student_ID,p->name,p->date,p->address,p->course,p->reason,p->check);
p=p->next;
}
printf("______________________________________________________________________________________________________________________\n\n");
}
8、pointer get(lklist head,int i):该函数用于按序号查找想要查找的考勤信息,并返回该结点,主要用于其它功能对考勤信息的操作。
//按序号查找
pointer get(lklist head,int i)
{
int j;
pointer p;
if(i==0) return head;
if(i<0) return NULL;
j=0;
p=head->next;
while(p!=NULL)
{
j++;
if(j==i) break;
p=p->next;
}
return p;
}
9、int Delete(lklist head , int i):该函数用于删除特定的考勤信息,主要根据输入的序号数,查找到该考勤信息并将其删除,删除成功返回1,否则返回0。
//删除运算
int Delete(lklist head , int i)
{
pointer p,q;
q=get(head,i-1);
if(q==NULL||q->next==NULL)
{
printf("非法删除位置\n");
return 0;
}
p=q->next;
q->next=p->next;
delete p;
return 1;
}
10、int no_Manager_Langing(struct ID All_ID[],int i):该函数用于非管理员登录,根据输入的帐号和密码,如果密码输入正确,则返回1,否则返回0。登录成功后,调用非管理员主菜单的函数,否则输出密码错误,登录失败。
//非管理员登陆函数 登陆成功返回 1 ,登陆失败返回 0
int no_Manager_Langing(struct ID All_ID[],int i)
{
char id [15]; //用于存放输入的账号
char pwd [15]; //用于存放输入的密码
int j = 0;
int sign = 0; //用于记录密码是否正确
printf("\n\n非管理员登陆\n");
printf("请输入账号:");
scanf("%s",id);
printf("请输入密码:");
scanf("%s",pwd);
for(j=0;j<i;j++)
{
if( All_ID[j].role==0 && strcmp(id,All_ID[j].id)==0 && strcmp(pwd,All_ID[j].pwd)==0 )
{
strcpy(ID,id);
sign = 1; //密码正确,令sign为 1
break;
}
else
{
sign = 0; //密码错误,令sign为 0
}
}
return sign;
}
11、int no_Manager_Login(struct ID All_ID[],int i):该函数用于非管理员和本班同学的注册,根据输入的帐号判断是否是非管理员和本班同学,若是,则注册成功,返回1,否则返回0。
//非管理员注册函数 注册成功返回 1 ,注册失败返回 0
int no_Manager_Login(struct ID All_ID[],int i)
{
char id [15]; //用于存放输入的账号
char pwd [15]; //用于存放输入的密码
char repwd [15]; //用于存放再次输入的密码
int j = 0;
int sign = 0; //用于记录注册是否成功
printf("\n非管理员注册\n");
printf("请输入学号:");
scanf("%s",id);
printf("请输入密码:");
scanf("%s",pwd);
printf("请再次确认密码:");
scanf("%s",repwd);
for(j=0;j<i;j++)
{
if( All_ID[j].role==0 && strcmp(id,All_ID[j].id)==0 && strcmp(pwd,repwd)==0 )
{
strcpy(All_ID[j].pwd,pwd);
sign = 1; //注册成功,令sign为 1
break;
}
else
{
sign = 0; //注册失败,令sing为 0
}
}
return sign;
}
12、void mangerMenu( struct ID All_ID[] , lklist head ):该函数为管理员主菜单输出函数,可以根据不同的选择,实现不同的功能。主要有,查看个人信息功能、修改密码功能、确认考勤信息功能、浏览所有学生的信息功能、浏览所有考勤信息功能、添加考勤信息功能、删除考勤信息功能、修改考勤信息功能、查询考勤信息功能、统计考勤信息功能。
//管理员菜单
void mangerMenu( struct ID All_ID[] , lklist head )
{
if(warning( head )!=0)
{
printf("\n你存在待确认的考勤信息!\n请前往确认!\n");
}
int chose=10;
while(chose!=0)
{
printf("\n 管理员菜单 \n");
printf("***********************************\n");
printf("* 1.查看个人信息 *\n");
printf("* 2.修改密码 *\n");
printf("* 3.确认考勤信息 *\n");
printf("* 4.浏览所有学生的信息 *\n");
printf("* 5.浏览所有考勤信息 *\n");
printf("* 6.添加考勤记录 *\n");
printf("* 7.删除考勤记录 *\n");
printf("* 8.修改考勤记录 *\n");
printf("* 9.查询考勤信息 *\n");
printf("* 10.统计考勤信息 *\n");
printf("* 11.重置其它学生密码 *\n");
printf("* 0.退出 *\n");
printf("***********************************\n");
printf("请输入功能的选项:");
scanf("%d",&chose);
switch(chose)
{
case 1: search( All_ID , head ) ;break; //查看个人信息
case 2: if( changePwd(All_ID)==1 ) //判断修改密码是否成功
{
printf("\n\n修改成功!\n\n");
}
else
{
printf("\n输入有误!请重新输入!\n\n");
}
writeFile_ID(All_ID,flag); //修改密码后,重新写入文件
break;
case 3: checkMessage( head ); //确认考勤信息
writeFile( head ); //确认考勤信息后,重新写入文件
break;
case 4: browse( All_ID , flag ) ;break; //浏览所有学生的信息
case 5: cout( head ) ;break; // 浏览所有考勤信息
case 6: insert(head,count) ; //添加考勤信息
init( All_ID , head ); //添加考勤信息后,重新统计考勤信息
count++; //添加考勤信息后,考勤记录加 1
break;
case 7: deletes( head ); //删除考勤记录
count--;
init( All_ID , head ); //删除考勤记录后,重新统计考勤信息
writeFile( head ); //删除考勤记录后,重新写入文件
break;
case 8: modify( head ); //修改考勤记录
init( All_ID , head ); //修改考勤记录后,重新统计考勤信息
writeFile( head ); //修改考勤记录后,重新写入文件
break;
case 9: inquiryMenu( head ) ;break; //查询考勤信息
case 10: statisticsMenu( All_ID ) ;break; //统计考勤信息
case 11: resetPwd(All_ID); //重置其它学生密码
writeFile_ID(All_ID,flag); //修改密码后,重新写入文件
break;
case 0: printf("\n欢迎下次使用!\n\n");
exit(0) ;
break;
default:printf("您输入的数据有误!\n请重新输入!\n");
}
}
}
13、void no_managerMenu( struct ID All_ID[] , lklist head ):该函数为非管理员主菜单输出函数,可以根据不同的选择,实现不同的功能。主要有,查看个人信息功能、修改密码功能、确认考勤信息功能、浏览所有考勤信息功能、查询考勤信息功能、统计考勤信息功能。
//非管理员菜单
void no_managerMenu( struct ID All_ID[] , lklist head )
{
if(warning( head )!=0)
{
printf("\n你存在待确认的考勤信息!\n请前往确认!\n");
}
int chose=0;
while(chose!=7)
{
printf("\n 非管理员菜单 \n");
printf("*******************************\n");
printf("* 1.查看个人信息 *\n");
printf("* 2.修改密码 *\n");
printf("* 3.确认考勤信息 *\n");
printf("* 4.浏览所有考勤信息 *\n");
printf("* 5.查询考勤信息 *\n");
printf("* 6.统计考勤信息 *\n");
printf("* 7.退出 *\n");
printf("*******************************\n");
printf("请输入功能的选项:");
scanf("%d",&chose);
switch(chose)
{
case 1: search( All_ID , head ) ;break; //查看个人信息
case 2: if( changePwd(All_ID)==1 ) //修改密码
{
printf("\n\n修改成功!\n\n");
}
else
{
printf("\n输入有误!请重新输入!\n\n");
}
writeFile_ID(All_ID,flag);
break;
case 3: checkMessage( head ); //确认考勤信息
writeFile( head ); //确认完考勤信息,写入文件
break;
case 4: cout( head ) ;break; // 浏览所有考勤信息
case 5: inquiryMenu( head ) ;break; //查询考勤信息
case 6: statisticsMenu( All_ID ) ;break; //统计考勤信息
case 7: printf("\n欢迎下次使用!\n\n");
exit(0);
break;
default:printf("您输入的数据有误!\n请重新输入!\n");
}
}
}
文章篇幅有限完整的代码我放到了文章的最后~~~有兴趣的同学可以看看
14、void checkMessage( lklist head ):该函数用于确认个人的考勤信息,根据实际情况,输出所有的待确认的考勤信息,如果考勤信息无误,则选择确认,若没有待确认的考勤信息,则不会输出。
//确认考勤信息
void checkMessage( lklist head )
{
int sign=0; //记录是否存在未确认的考勤信息
char chose;
pointer p;
p=head->next;
printf("\n______________________________________________________________________________________________________________________\n");
printf(" 编号 学号 姓名 日期 地点 课程节数 缺勤性质 确认情况\n");
while(p!=NULL)
{
if( strcmp(ID,p->student_ID)==0 && strcmp(p->check,"待确认")==0 ) //判断是否存在待确认的考勤信息
{
sign++;
printf(" %-5d %-15s %-10s %-9s %-12s %-13s %-9s %-10s\n",p->number,p->student_ID,p->name,p->date,p->address,p->course,p->reason,p->check);
}
p=p->next;
}
printf("______________________________________________________________________________________________________________________\n");
if(sign!=0) //判断是否存在考勤信息,如果存在,则执行是否确认该考勤信息
{
printf("是否确认该考勤信息(Y/N):");
getchar(); //用于清除回车
scanf("%c",&chose); //需要输入Y或者N
while(chose!='Y'&&chose!='N') //容错处理,用于判断是否是Y或者N,不是则需要重新输入
{
printf("输入错误,请重新输入(Y/N):");
getchar(); //用于清除回车
scanf("%c",&chose); //需要重新输入Y或者N
}
if(chose=='Y')
{
p=head->next; //将指针重新指向头部
while(p!=NULL)
{
if( strcmp(ID,p->student_ID)==0 && strcmp(p->check,"待确认")==0 )
{
strcpy(p->check,"已确认"); //确认考勤信息
}
p=p->next;
}
printf("已确认!\n");
}
else
{
printf("已退出!\n");
}
}
else
{
printf("没有待确认的考勤信息!\n\n");
}
}
15、void modify( lklist head ):该函数用于修改考勤信息,根据实际情况,输入想要修改的考勤信息编号,该函数会提供一个菜单功能,选择某一项信息进行修改,包括日期、地点、课程节数和性质。修改成功会输出修改后的信息。
//修改考勤信息
void modify( lklist head )
{
pointer p;
int a=0; //用于记录,是否找到想要修改的考勤信息
int b=0; //用于是否退出修改菜单
int sign=0; //用于记录,是否修改完考勤信息
int chose=0; //用于菜单的选择功能
int numbers=0; //用于存储输入的编号
cout( head );
while(a==0)
{
printf("请输入想要修改的考勤信息的编号:");
scanf("%d",&numbers);
p=head->next;
while(p!=NULL)
{
if(numbers==p->number)
{
a++;
printf("______________________________________________________________________________________________________________________\n");
printf(" 编号 学号 姓名 日期 地点 课程节数 缺勤性质 确认情况\n");
printf(" %-5d %-15s %-10s %-9s %-12s %-13s %-9s %-10s\n",p->number,p->student_ID,p->name,p->date,p->address,p->course,p->reason,p->check);
printf("______________________________________________________________________________________________________________________\n\n");
break;
}
p=p->next;
}
if(a==0) //对没有查询到信息进行输出
printf("\n您想要修改的信息不存在! 请重新输入!\n\n");
}
printf("\n 选择修改的类型 \n");
printf("***************************************\n");
printf("* 1 修改日期 *\n");
printf("* 2 修改地点 *\n");
printf("* 3 修改课程节数 *\n");
printf("* 4 修改性质 *\n");
printf("* 5 退出 *\n");
printf("***************************************\n");
while(b==0)
{
printf("请输入修改的类型:");
scanf("%d",&chose);
switch(chose)
{
case 1: printf("请输入修改后的日期(**月**号):");
scanf("%s",p->date);
b++;
sign++;
break;
case 2: printf("请输入修改后的地点:");
scanf("%s",p->address);
b++;
sign++;
break;
case 3: printf("请输入修改后的课程节数:");
scanf("%s",p->course);
b++;
sign++;
break;
case 4: printf("请输入修改后的性质(迟到/缺席/请假):");
scanf("%s",p->reason);
b++;
sign++;
break;
case 5: printf("已退出修改!\n");
b++;
break;
default:printf("您输入的数据有误! 请重新输入!\n\n");
}
}
if(sign!=0)
{
printf("______________________________________________________________________________________________________________________\n");
printf(" 编号 学号 姓名 日期 地点 课程节数 缺勤性质 确认情况\n");
printf(" %-5d %-15s %-10s %-9s %-12s %-13s %-9s %-10s\n",p->number,p->student_ID,p->name,p->date,p->address,p->course,p->reason,p->check);
printf("______________________________________________________________________________________________________________________\n");
printf("修改成功!\n\n");
}
}
步骤四:功能测试
就随便测了几个功能,边测边改bug
总结
程序优点:基本实现要求的所有功能,程序能较好地运行,而且用户输入错误时有保障机制,能让用户重新输入数据。在运行过程中,有良好的输出界面,能清晰地显示。每次的输入都有提醒,输入有误时,也有保障机制,能让程序稳定执行下去。每个子功能,都存在子菜单,为实现不同的功能,操作方便。在内部代码的实现上,功能分明,思路清晰。实现了所需的功能操作和更加有序的程序代码。将不同的功能封装在不同的函数中,调用调试方便,也方便后期的修改。在使用时,简单方便,通俗易懂,效率高。
程序不足:一、在时间复杂度上,时间复杂度有点高,优化程度不足,多处使用循环嵌套语句。二、在空间维度上,定义了不少的全局变量,用于统计数据和记录,占用了不少的内存空间。不是全部的数据都采用链表储存,部分数据采用了数组的形式储存,为了方便经常查找统计。三、在功能实现方面上,实现了全部的功能。但在某些功能的实现上,设置得不够合理,使用不够方便。如在修改和删除功能上不能批量操作,只能逐条操作。在某些输入操作时,必须按照提示输入,否则不能达到理想的效果。四、在代码量上,实现整个系统功能的代码量过长,并且某些功能的代码冗长,重复的代码很多,没有做到很好地减化代码量,若后期想修改某些功能时,修改量比较大,比较复杂。