C语言课程设计——学生考勤管理系统

article/2025/11/6 11:24:56

C语言课程设计——学生考勤管理系统

题目要求:

学生考勤管理系统设计
(1)问题描述
考勤信息记录了学生的缺课情况,它包括:缺课日期、第几节课、课程名称、学生姓名、缺课类型(迟到、早退、请假及旷课)。采取模块化方式进行程序设计,要求程序的功能设计、数据结构设计及整体结构设计合理。
系统以菜单界面方式工作,运行界面友好,演示程序以用户和计算机的对话方式进行。
(2)功能要求
录入学生的缺课记录;
修改某个学生的缺课记录;
查询某个学生的缺课情况;
统计某段时间内,旷课学生姓名及旷课次数,按旷课次数由多到少排序;
统计某段时间内,有学生旷课的课程及旷课人次,按旷课人次由多到少排序;
系统以菜单方式工作。
保存功能:可将当前系统中各类记录存入文件中,存入方式任意。
读取功能:可将保存在文件中的信息读入到当前系统中,供用户进行使用。

设计文档:

目 录

一、需求分析…
(1)总体分析…
(2)细化分析…
(3)操作分析…
二、主调函数定义及文件结构划分…
(1)主调函数定义…
(2)文件结构划分…
三、菜单指令编码…
四、事件流效果图…
五、存储结构设计…
(1)存储结构分析…
(2)存储结构确定…
六、算法效率分析…
七、开发记录与总结…

一、需求分析
(1)总体分析:
本系统为一个基于CMD命令窗口的学生考勤管理系统,要求能够记录、删除、修改、查询学生的考勤信息(考勤信息包括学生学号、姓名、所缺课名、缺课节次、缺课类型、缺课时间),并且能够统计学生的历史考勤信息以及每堂课的到课情况,能够通过本系统便利的实现对学生考勤情况的把控,要求操作界面友好,便捷,有一定的容错率;

系统大体流程如下:
在这里插入图片描述

(2)细化分析:
本系统可分为功能性和非功能性两部分;

功能性部分:
功能性部分可分为两个模块,即编辑模块和统计模块;

·编辑模块:编辑模块需要实现增、删、改三个功能;

首先对于增功能,总体考虑到两种插入方式:
第一种方式,通过一份名单,在名单并未通过排序的情况下进行插入,这种方式在有同一人多次缺课的情况下无疑会造成名字、学号的大量重复输入,但不要求用户对名单进行处理;
第二种方式,在对一份名单通过名字排序之后进行插入,用户只需输入一次名字和学号,之后每一次对于同一个学生的缺课信息只需要输入主体信息即可,这种方式省去了用户大量的操作冗余,提高了用户体验,因此推荐采用第二种插入方式,当然这种方式要求用户对名单进行一个统筹;

对于删、改功能,考虑到三种编辑方式:
第一种方式,让用户输入需要编辑的缺勤信息记录,然后对其进行删除或修改;这种方式同样存在用户操作上的冗余;
第二种方式,每次进行编辑之前先将缺勤信息列表展示出来,让用户选择一条记录,然后进行删除或修改,这种方式省去了用户操作上的冗余,本系统采用该种方式;
第三种方式,在用户进行修改删除之前让用户输入一个时间段和对象进行筛选,然后再结合第二种方式进行修改删除,这种方式既省去了大部分操作上的冗余,也在相当程度上避免了将筛选工作交给用户的情况(当然,当数据量非常庞大时仍然无法避免,因此建议在后续版本中增加定期清除记录的功能),建议采取第三种方式来实现修改、删除功能;

·统计模块:统计模块需要实现两个功能,查询和排序;
我将查询功能分到了统计模块,第一是因为查询功能并不需要对历史文件进行编辑;第二是因为查询功能实际上只是统计功能的一个缩减而已(省去排序);

对于查询功能,在本系统的设计中是使用最为频繁的功能,按照本系统的设计思路,无论是用户直接进行查询,还是需要进行修改、删除、统计都需要对记录进行查询;因此如何加快查询速度就成了一个很重要的问题;
查询功能的实现方式考虑到三种方式:
第一种方式,将所有学生的缺课记录全部存储在一个文件,这样会造成一些问题。首先当用户直接使用查询功能查询某个学生的缺课记录时,为了避免数据缺漏,必须将整个文件从头到尾读完,换成数据库的说法就是,无法避免全表扫描,这样的方式效率极慢,尤其当数据量较大的时候;当用户使用删除、修改功能而调用到查询功能时同样无法避免这样的问题;
第二种方式,考虑建立数据结构,比如InnoDB索引中使用的B+ tree数据结构,但考虑到本系统的插入、修改、删除功能同样使用频繁,对于本系统的体量来说,维护B+ tree的系统开销所带来的弊端还要大于全表遍历,因此在本系统并不适合使用B+ tree;
第三种方式,从文件的存储入手,每个学生对应一个文件;这种方式无论是用户单独使用查询功能还是使用修改、删除时调用都能够非常有效的避免全表扫描,并且并不需要任何维护的开销,对于本系统来说是最适合的方式;

对于两种统计功能,将其排序后调用查询功能输出至控制台即可;

非功能性部分:非功能性部分可分为菜单选择模块和进程判断模块;
对于菜单选择模块,应根据用户的指令编码进入对应功能的子菜单;
对于进程判断模块,为了确保一定的容错率以及环境友好,应该能让用户自行把控程序进程;

二、主调函数定义及文件结构划分:
(1)主调函数定义:
根据以上分析,可定义主调函数如下:
在这里插入图片描述

(2)文件结构划分:
本系统根据对数据的操作将文件分为5个部分:

在这里插入图片描述

三、菜单命令编码:
0:void terminate() //退出系统;
1:void operate() //选择编辑模块;
4:void insert() //编辑模块下的插入功能;
5:void update() //编辑模块下的修改功能;
6:void delete() //编辑模块下的删除功能;
2:void select() //选择查询功能;
3:void typeJudge() //选择统计模块;
7:void stuAbsentInfoSort() //统计模块下的一种统计方式;
8:void couAbsentInfoSort() //统计模块下的一种统计方式;
9:void mainMenuDisplay() //返回主菜单;

四、事件流效果图
根据以上指令编码,可确定事件流如下图:
在这里插入图片描述

五、存储结构设计
(1)存储结构分析:
使用结构体存放学生缺勤信息;
对于单人信息查询考虑实际情况并不会有太多的数据记录,因此使用普通数组存放即可;

对于统计模块,考虑总体数据量较大且无法估计,为了避免造成空间浪费或者越界,使用链表结构进行储存;同时考虑第二种统计方式根据本系统的设计方式在进行筛选时会出现双重循环的情况,为了避免出现n^2的时间复杂度,可使用双向链表进行双向筛选,达到降低时间复杂度的目的;

同时对于使用频繁的变量可使用static进行修饰,从而避免重复创建,每次使用完将其初始化即可;

(2)存储结构确定:
基于以上分析,可确定存储结构如下;
typedef struct {
char name[NAME_SIZE];
char id[ID_SIZE];
char course[COURSE_SIZE];
char courseId[COURSE_ID_SIZE];
char absentType[ABSENT_TYPE_SIZE];
char absentTime[ABSENT_TIME_SIZE];
}AbsentInfo;//存放学生缺勤信息;
static AbsentInfo absentInfo = {0};

typedef struct {
int serialNumber;
AbsentInfo info;
}PersonalAbsInfo;//建立删除、修改时名单;
static PersonalAbsInfo pAbsInfo[20] = {0};

typedef struct Link {
char name[NAME_SIZE];
int number;
struct Link * next;
}AbsentInfoLink;//统计模块学生缺勤信息单链表;

typedef struct course {
char name[COURSE_SIZE];
int number;
struct course * previous;
struct course * next;
}CourseLink;//统计模块课堂到课信息双链表;

static const char * search = “D:\CourseDesign\cmake-build-debug\list\*txt”;
//文件存放地址;

六、算法效率分析
前面已经确定,对于单人信息查询使用普通数组,对于统计模块的信息查询使用单链表和双链表,这里有几个关于效率需要考虑的地方;

(1)多组输入问题:
当用户进行多组数据插入时,其输入数据量是无法估计的,那么如果通过数组来进行存储,然后再写入文件的方式来插入势必会造成一些不必要的空间浪费;
当然也可以建立链表,但根据前面的设计,如果需要输入3个人的缺课记录就需要建立3次链表,10个人的记录就需要建立十次链表,考虑到一个链表可能只有两三个节点,这样的方式实在是有些划不来;
本系统采用第三种方式,对于多组输入循环调用单条数据的插入,这样既避免了不必要的空间浪费,也避免了不要的系统开销;

(2)统计模块的排序问题:
对于统计模块的两种统计方式都是需要进行排序的,这里需要对排序方式进行一个选择;
常用的排序方式有三种:冒泡、选择、递归,冒泡和选择的平均时间复杂度都为n^2,递归为n*logn因此选用后者;

七、开发记录与总结
(1)开发记录:
开发周期:7天;
Day 1:
整理逻辑、明确需求,理清流程;

Day 2:
流程图有错误,进行修改;功能模块划分、分出主调函数;

Day 3:
划分头文件,声明函数,设计菜单;

Day 4:
插入、查找功能完成,但clion开发环境出现问题,无法链接.c文件和.o文件,未解决;

Day 5:
修改、删除功能完成,clion链接问题解决;
无法链接原因:
clion创建的项目通过CMakeList文件来进行管理,每写一个.c文件都需要在CMakeList中用对应的语法格式书写配置,将.c文件加入到管理中,否则无法链接;
解决:
关于CMakeList的书写方法说明材料较少,暂未学习,最终将header文件夹和source文件夹加入到target目标文件夹下,挂在main.c文件名下被CMakeList一起托管;

Day 6:
关于对时间的对比遇到难题,时间无法通过strcmp()比较,需要转换为t_time时间戳。但调用time.h下的mktime()函数始终返回-1,查遍资料并未找到原因和解决办法,开发陷入停滞…
解决:
当使用mktime(struct tm time)返回时间戳时, struct tm time中的年份至少要为70;
原因:
struct tm中的year是从1900年到现在的年份,传到mktime()中被转为真正的年份,mktime只能返回从1970年开始的时间戳,若转换后年份小于1970则异常返回-1;

Day 7:
功能测试、调试Bug;

(2)总结:
1、对于修改、删除功能,本系统的设计上出现了一个巨大的缺陷:即将数据筛选的功能交给了用户;根据本系统的设计方式,虽然减少了用户操作上的冗余,但当数据量较大时却需要用户自己对需要修改或删除的数据进行查找,这无疑会对用户的体验感产生致命打击;
解决:正确的方式应该是让用户输入需要编辑的对象,然后指定一个时间段,通过这两层筛查之后再采用现在的设计方式,这样虽然仍然是将查找功能交给了用户,但经过筛选之后所剩下的基本就是用户的目标记录了;

2、对于统计模块的排序功能,本系统的设计陷入了一个误区:先链表再排序;然而实际上这两者是可以同时进行的。采用双向链表的大碓顶链接方式,在节点链接上之前先进行位置查找,链表完成即排序完成;这种方式不光省去了大量代码,并且其时间复杂度为n^2/8,甚至要优于快速排序法;

3、本系统虽然在非功能性部分提供了返回机制,但并未对用户的输入进行筛查,这可能会导致数据的不合理性,从而导致系统崩溃,这都需要在后续版本中进行优化;

注意:

这次课程设计我使用的开发环境是Jetbrain系列的Clion,主要是Java用Idea已经习惯Jetbrain的审美了,用Code block感觉很丑。而且Code block拿来打算法还可以,做开发的话实在是麻烦得很,跟Clion根本无法比。如果有同学向Copy到Code block去的话可能会出一些问题,因为有些Clion支持的机制Code block是不支持的。

目录结构:

请添加图片描述

代码实现:

  • data.h
//
// Created by 86199 on 2021/6/23.
//#ifndef COURSEDESIGN_DATA_H
#define COURSEDESIGN_DATA_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>#define NAME_SIZE 10
#define ID_SIZE 15
#define COURSE_SIZE 10
#define COURSE_ID_SIZE 5
#define ABSENT_TYPE_SIZE 10
#define ABSENT_TIME_SIZE 30
/*#define SwapNode (p1, p2, node) \
{ \node -> number = p1 -> number;\strcpy (node -> name, p1 -> name);\p1 -> number = p2 -> number;\strcpy (p1 -> name, p2 -> name);\p2 -> number = node -> number;\strcpy (p2 -> name, node -> name);\free (node);\
}*/typedef struct {char name[NAME_SIZE];char id[ID_SIZE];char course[COURSE_SIZE];char courseId[COURSE_ID_SIZE];char absentType[ABSENT_TYPE_SIZE];char absentTime[ABSENT_TIME_SIZE];
}AbsentInfo;
static AbsentInfo absentInfo = {0};typedef struct {int serialNumber;AbsentInfo info;
}PersonalAbsInfo;
static PersonalAbsInfo pAbsInfo[20] = {0};static time_t timeBegin;
static time_t timeEnd;typedef struct Link {char name[NAME_SIZE];int number;struct Link * next;
}AbsentInfoLink;typedef struct {AbsentInfoLink * head;AbsentInfoLink * end;
}Point;
Point point;typedef struct course {char name[COURSE_SIZE];int number;struct course * previous;struct course * next;
}CourseLink;typedef struct {CourseLink * head;CourseLink * end;
}CoursePoint;
CoursePoint coursePoint;static const char * search = "D:\\CourseDesign\\cmake-build-debug\\list\\*txt";static char Name[NAME_SIZE] = {0};
static char Id[ID_SIZE] = {0};
static char Way[2] = {0};#endif //COURSEDESIGN_DATA_H
  • dataInput.h
//
// Created by 86199 on 2021/6/22.
//#ifndef COURSEDESIGN_DATAINPUT_H
#define COURSEDESIGN_DATAINPUT_H#include "data.h"int codeInput();void tmInput(struct tm * time);void absentInfoInput(char name[], AbsentInfo * absentInfo, char id[]);time_t timeInput();Point stuAbsentInfoInput();int couAbsentNameJudge(char courseName[]);CoursePoint couAbsentInfoInput();int absentTypeJudge(char absentType[]);#endif //COURSEDESIGN_DATAINPUT_H
  • dataOutput.h
//
// Created by 86199 on 2021/6/22.
//#ifndef COURSEDESIGN_DATAOUTPUT_H
#define COURSEDESIGN_DATAOUTPUT_H#include "data.h"void mainMenuDisplay();void operateMenuDisplay();void typeMenuDisplay();void continueOrExit();void absentInfoOutput(char fileName[], char way[], PersonalAbsInfo personalInfo[]);void infoInputExample();void timeInputExample();#endif //COURSEDESIGN_DATAOUTPUT_H
  • dataProcess.h
//
// Created by 86199 on 2021/6/22.
//#ifndef COURSEDESIGN_DATAPROCESS_H
#define COURSEDESIGN_DATAPROCESS_H#include <stdio.h>void select();FILE * fileOpen(char fileName[], char way[]);void insert(char name[], char way[], char id[]);void alter(char name[], char id[]);void Delete(char name[]);void sureAmount(char name[], char way[], char id[]);void fileInsert(char name[], char way[]);void fileUpdate(char name[], char id[]);void fileDelete(char name[]);#endif //COURSEDESIGN_DATAPROCESS_H
  • service.h
//
// Created by 86199 on 2021/6/22.
//#ifndef COURSEDESIGN_SERVICE_H
#define COURSEDESIGN_SERVICE_H#include "data.h"void terminate();void progressJudge(int code);void operateJudge();void typeJudge();int idJudge(char name[]);int timeRangeJudge(AbsentInfo info);void swapSingleNode (AbsentInfoLink * p1, AbsentInfoLink * p2);void singleLinkQuickSort(AbsentInfoLink * pHead, AbsentInfoLink * pEnd);void pointJudge();void timeRangeInput();void stuAbsentInfoSort();void swapDoubleNode(CourseLink * p1, CourseLink * p2);void doubleLinkQuickSort(CourseLink * pHead, CourseLink * pEnd);void courseAbsentInfoSort();void menuJudge(int code);void stuLinkOutput();void couAbsInfoOutput();#endif //COURSEDESIGN_SERVICE_H
  • dataInput.c
//
// Created by 86199 on 2021/6/24.
//#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <io.h>
#include <sys/time.h>
#include <stdint.h>
#include "../header/data.h"
#include "../header/dataProcess.h"
#include "../header/service.h"
#include "../header/dataInput.h"int codeInput() {int code = 0;scanf ("%d", &code);if (code > 10 || code < 0) {printf ("请输入正确指令!\n");codeInput();}return code;
}void tmInput(struct tm * time) {scanf ("%d", &time -> tm_mon);getchar();scanf ("%d", &time -> tm_mday);getchar();scanf ("%d", &time -> tm_hour);getchar();scanf ("%d", &time -> tm_min);getchar();
}void absentInfoInput(char name[], AbsentInfo * info, char id[]) {struct tm time = {0};strcpy(info -> name, name);strcpy(info -> id, id);scanf ("%s%s%s", info -> course, info -> courseId, info -> absentType);tmInput(&time);char cur_time[20] = {0};snprintf(cur_time, 20, "%02d-%02d %02d:%02d", time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min);strcpy(info -> absentTime, cur_time);
}time_t timeInput() {struct tm cur_time = {0};tmInput(&cur_time);cur_time.tm_year = 70;return mktime(&cur_time);
}Point stuAbsentInfoInput() {FILE * stream;long handle ;int k;struct _finddata_t file;k = handle = _findfirst (search, &file);AbsentInfoLink * head = NULL, * end, * node = NULL;head = (AbsentInfoLink *) malloc (sizeof (AbsentInfoLink));memset(head, 0, sizeof (AbsentInfoLink));end = head;while (k != -1) {strcpy (Name, strtok(file.name, "."));char name[NAME_SIZE] = {0};strcpy (name, Name);stream = fileOpen(name, "r");node = (AbsentInfoLink *) malloc (sizeof (AbsentInfoLink));memset(node, 0, sizeof (AbsentInfoLink));strcpy (node -> name, Name);int num = 0;while (fread (&absentInfo, sizeof (AbsentInfo), 1, stream)) {if (! timeRangeJudge(absentInfo)) {memset (&absentInfo, 0, sizeof (AbsentInfo));continue;}num ++;}node -> number = num;end -> next = node;end = node;k = _findnext(handle, &file);}end -> next = NULL;fclose (stream);point.head = head;point.end = end;return point;
}CoursePoint couAbsentInfoInput() {FILE * stream;long handle;int k;struct _finddata_t file;k = handle = _findfirst (search, &file);CourseLink * node = NULL, * head = NULL, * end = NULL;head = (CourseLink *) malloc (sizeof (CourseLink));memset(head, 0, sizeof (AbsentInfoLink));end = head;while (k != -1) {strcpy (Name, strtok (file.name, "."));char name[NAME_SIZE] = {0};strcpy (name, Name);stream = fileOpen(name, "r");node = (CourseLink *) malloc(sizeof (CourseLink));memset(node, 0, sizeof(CourseLink));while (fread (&absentInfo, sizeof (AbsentInfo), 1, stream)) {if (timeRangeJudge(absentInfo) && ! absentTypeJudge(absentInfo.absentType) && ! couAbsentNameJudge(absentInfo.course)) {strcpy (node -> name, absentInfo.course);node -> number ++;end -> next = node;node -> previous = end;end = node;coursePoint.head = head;coursePoint.end = end;node = (CourseLink *) malloc(sizeof (CourseLink));memset(node, 0, sizeof(CourseLink));}memset (&absentInfo, 0, sizeof (AbsentInfo));}k = _findnext (handle, &file);}fclose (stream);return coursePoint;
}int couAbsentNameJudge(char courseName[]) {CourseLink * start = coursePoint.head, * finish = coursePoint.end;while (start != finish) {if (! strcmp (start -> name, courseName)) {start -> number ++;return 1;}elseif (! strcmp (finish -> name, courseName)) {finish -> number ++;return 1;}start = start -> next;finish = finish -> previous;}return 0;
}int absentTypeJudge(char absentType[]) {return strcmp(absentType, "truant");
}
  • dataOutput.c
//
// Created by 86199 on 2021/6/24.
//#include <stdio.h>
#include "../header/data.h"
#include "../header/dataProcess.h"
#include "../header/service.h"
#include "../header/dataOutput.h"void mainMenuDisplay() {printf ("************************************************************\n");printf ("                      学生考勤管理系统\n");printf ("************************************************************\n");printf ("请输入指令...\n");printf ("0、结束运行        ");printf ("1、编辑记录        ");printf ("2、查询记录        ");printf ("3、查询统计\n");printf ("(注:接下来一切数据请输入英文...)\n");
}void operateMenuDisplay() {printf ("请选择编辑类型:\n");printf ("4、插入记录        ");printf ("5、修改记录        ");printf ("6、删除记录\n");}void typeMenuDisplay() {printf ("请选择统计类型:\n");printf ("7、学生到课情况统计      ");printf ("8、课程到课情况统计\n");
}void infoInputExample() {printf ("(格式:课程 节次 缺课类型 缺课时间)\n");printf ("(样例:JAVA 1 truant 6-25/10:10)\n");printf ("缺课类型:(旷课 == truant, 请假 == askLeave, 迟到 == late, 早退 == early)\n");
}void timeInputExample() {printf ("请输入查询时间段:\n");printf ("(样例:6-26/11:00)\n");printf ("(注:若时间输入错误,输入完毕后可选择返回上一级...)\n");
}void continueOrExit() {printf ("请进行下一步操作...\n");printf ("0、退出系统      9、返回菜单\n");int code;scanf ("%d", &code);if (code == 0) {menuJudge(0);}else {mainMenuDisplay();scanf ("%d", &code);menuJudge(code);}
}void absentInfoOutput(char fileName[], char way[], PersonalAbsInfo personalInfo[]) {memset(personalInfo, 2, sizeof (PersonalAbsInfo));char name[NAME_SIZE] = {0};strcpy(name, fileName);FILE * stream;stream = fileOpen(name, way);if (stream == NULL) {printf ("该学生暂无缺勤记录,请重新输入!\n");menuJudge(2);}int index = 0;if (stream != NULL)printf ("序号          姓名          学号          课程          节次          缺课类型        缺课时间\n");while (fread(&absentInfo, sizeof(AbsentInfo), 1, stream)) {index ++;printf ("%-10d   %-10s   %-10s   %-10s   %-10s   %-10s   %-10s\n", index, absentInfo.name, absentInfo.id, absentInfo.course, absentInfo.courseId, absentInfo.absentType, absentInfo.absentTime);personalInfo[index].serialNumber = index;personalInfo[index].info = absentInfo;memset(&absentInfo, 0, sizeof(AbsentInfo));}
}
  • dataProcess.c
//
// Created by 86199 on 2021/6/24.
//#include <stdio.h>
#include <string.h>
#include "../header/service.h"
#include "../header/data.h"
#include "../header/dataProcess.h"
#include "../header/dataOutput.h"
#include "../header/dataInput.h"void select() {printf ("请输入查询对象:\n");scanf ("%s", Name);progressJudge(2);absentInfoOutput(Name, "r", pAbsInfo);continueOrExit();
}void insert(char name[], char way[], char id[]) {progressJudge(1);sureAmount(name, way, id);
}void alter(char name[], char id[]) {progressJudge(1);infoInputExample();absentInfoOutput(name, "r", pAbsInfo);fileUpdate(name, id);
}void Delete(char name[]) {progressJudge(1);absentInfoOutput(name, "r", pAbsInfo);fileDelete(name);
}void sureAmount(char name[], char way[], char id[]) {infoInputExample();printf ("您需要插入几条记录?\n");int n;scanf ("%d", &n);if (n > 1) {strcpy(way, "a");}printf ("请输入缺勤信息:\n");for (int i = 0; i < n; i ++) {memset(&absentInfo, 0, sizeof (AbsentInfo));absentInfoInput(name, &absentInfo, id);fileInsert(name, way);}continueOrExit();
}void fileInsert(char name[], char way[]) {char n[NAME_SIZE] = {0};strcpy(n, name);FILE * stream = fileOpen(n, way);fwrite(&absentInfo, sizeof (AbsentInfo), 1, stream);printf ("插入完毕!\n");fclose(stream);
}void fileUpdate(char name[], char id[]) {printf ("请输入修改记录序号:\n");int serialNumber;scanf ("%d", &serialNumber);printf ("请输入缺勤信息:\n");for (int i = 1; pAbsInfo[i].serialNumber != 0; i ++) {if (pAbsInfo[i].serialNumber == serialNumber) {absentInfoInput(pAbsInfo[i].info.name, &absentInfo, id);pAbsInfo[i].info = absentInfo;break;}}char n[NAME_SIZE] = {0};strcpy(n, name);FILE * stream = fileOpen(n, "w");for (int i = 1; pAbsInfo[i].serialNumber != 0; i ++) {fwrite (&pAbsInfo[i].info, sizeof (AbsentInfo), 1, stream);}printf ("修改完毕!\n");fclose(stream);continueOrExit();
}void fileDelete(char name[]) {printf ("请输入删除记录序号:\n");char n[NAME_SIZE] = {0};strcpy(n, name);int serialNumber;scanf ("%d", &serialNumber);FILE * stream = fileOpen(n, "w");for (int i = 1; pAbsInfo[i].serialNumber != 0; i ++) {if (pAbsInfo[i].serialNumber == serialNumber) {continue;}fwrite (&pAbsInfo[i].info, sizeof (AbsentInfo), 1, stream);}printf ("删除完毕!\n");fclose(stream);continueOrExit();
}FILE * fileOpen(char fileName[], char way[]) {char direction[55] = "D:\\CourseDesign\\cmake-build-debug\\list\\";strcat(direction, strcat(fileName, ".txt"));FILE * stream = NULL;stream = fopen(direction, way);if (strcmp (way, "r") != 0 && stream == NULL) {printf ("can't open file!");exit(0);}return stream;
}
  • service.c
//
// Created by 86199 on 2021/6/24.
//#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include "../header/data.h"
#include "../header/dataProcess.h"
#include "../header/dataOutput.h"
#include "../header/dataInput.h"
#include "../header/service.h"void menuJudge(int code) {switch(code) {case 0 : terminate();case 1 : operateJudge(); break;case 2 : select(); break;case 3 : typeJudge(); break;case 4 : insert(Name, Way, Id); break;case 5 : alter(Name, Id); break;case 6 : Delete(Name); break;case 7 : stuAbsentInfoSort(); break;case 8 : courseAbsentInfoSort();break;case 9 : mainMenuDisplay(); break;}
}void terminate() {printf ("感谢您的使用!\n");exit(0);
}void operateJudge() {printf ("请输入学生姓名:\n");scanf ("%s", Name);printf ("请输入学号:\n");scanf ("%s", Id);progressJudge(1);operateMenuDisplay();char name[ID_SIZE] = {0};strcpy(name, Name);int sign = idJudge(name);int code = codeInput();if (code == 4) {if (sign) {strcpy(Way, "a");}else {strcpy(Way, "w");}menuJudge(code);}else {if (sign) {menuJudge(code);}else {printf("该学生暂无缺勤记录,请重新输入编辑对象!\n");operateJudge();}}
}void typeJudge() {typeMenuDisplay();int code = codeInput();progressJudge(3);menuJudge(code);
}void stuAbsentInfoSort() {timeRangeInput();point = stuAbsentInfoInput();pointJudge();singleLinkQuickSort(point.head -> next, point.end);stuLinkOutput();continueOrExit();
}void courseAbsentInfoSort() {timeRangeInput();coursePoint = couAbsentInfoInput();pointJudge();doubleLinkQuickSort(coursePoint.head -> next, coursePoint.end);couAbsInfoOutput();continueOrExit();
}void singleLinkQuickSort(AbsentInfoLink * pHead, AbsentInfoLink * pEnd) {if (pHead == NULL || pEnd == NULL || pHead == pEnd) {return ;}AbsentInfoLink * p1 = pHead, * p2 = p1 -> next;int num = pHead -> number;while (p2 != pEnd -> next && p2 != NULL) {if (p2 -> number > num) {p1 = p1 -> next;swapSingleNode (p1, p2);}p2 = p2 -> next;}swapSingleNode (pHead, p1);singleLinkQuickSort (pHead -> next, p1);singleLinkQuickSort (p1 -> next, pEnd);
}void stuLinkOutput() {AbsentInfoLink * node = NULL;node = point.head -> next;printf ("序号          姓名          缺课次数\n");int index = 0;while (node != NULL) {printf ("%-10d   %-10s   %-10d\n", ++ index, node -> name, node -> number);node = node -> next;}continueOrExit();
}void doubleLinkQuickSort(CourseLink * pHead, CourseLink * pEnd) {if (pHead == NULL || pEnd == NULL || pHead == pEnd) {return ;}CourseLink * first = pHead, * last = pEnd;int number = pHead -> number;while (first != last) {while (last -> number <= number && first != last) {last = last -> previous;}swapDoubleNode(first, last);while (first -> number >= number && first != last) {first = first -> next;}swapDoubleNode(first, last);}if (first != pHead) {doubleLinkQuickSort(pHead, first->previous);}if (last != pEnd) {doubleLinkQuickSort(last->next, pEnd);}
}void couAbsInfoOutput() {CourseLink * node = NULL;node = coursePoint.head -> next;printf ("序号       课程                 缺课人数\n");int index = 0;while (node != NULL) {printf ("%-10d%-20s%-10d\n", ++index, node -> name, node -> number);node = node -> next;}continueOrExit();
}void swapSingleNode (AbsentInfoLink * p1, AbsentInfoLink * p2) {AbsentInfoLink * node = (AbsentInfoLink *) malloc (sizeof (AbsentInfoLink));node -> number = p1 -> number;strcpy (node -> name, p1 -> name);p1 -> number = p2 -> number;strcpy (p1 -> name, p2 -> name);p2 -> number = node -> number;strcpy (p2 -> name, node -> name);free (node);
}void swapDoubleNode(CourseLink * p1, CourseLink * p2) {CourseLink * node = (CourseLink *) malloc (sizeof (CourseLink));node -> number = p1 -> number;strcpy (node -> name, p1 -> name);p1 -> number = p2 -> number;strcpy (p1 -> name, p2 -> name);p2 -> number = node -> number;strcpy (p2 -> name, node -> name);free (node);
}void progressJudge(int code) {printf ("1:下一步          0:返回上一级          3:退出程序\n");int n;scanf ("%d", &n);if (n == 0) {menuJudge(code);}elseif (n == 3) {exit(0);}
}int idJudge(char name[]) {intptr_t handle;struct _finddata_t file = {0};handle = _findfirst(search, &file);if (handle == -1) {return 0;}strcat(name, ".txt");if (! strcmp(name, file.name)) {return 1;}memset(&file, 0, sizeof (struct _finddata_t));while (! _findnext(handle, &file)) {if (! strcmp(name, file.name)) {return 1;}memset(&file, 0, sizeof (struct _finddata_t));}return 0;
}int timeRangeJudge(AbsentInfo info) {struct tm cur_time = {0};sscanf(info.absentTime, "%2d-%2d %2d:%2d", &cur_time.tm_mon, &cur_time.tm_mday, &cur_time.tm_hour, &cur_time.tm_min);cur_time.tm_year = 70;time_t time = mktime(&cur_time);if (time >= timeBegin && time <= timeEnd) {return 1;}else {return 0;}
}void pointJudge() {if (point.head == NULL && point.end == NULL && coursePoint.head == NULL && coursePoint.end == NULL) {printf ("暂无该时间段记录,请重新输入...\n");menuJudge(3);}
}void timeRangeInput() {timeInputExample();printf ("Begin:\n");timeBegin = timeInput();printf ("End:\n");timeEnd = timeInput();progressJudge(3);
}
  • main.c
#include <stdio.h>
#include "../header/dataOutput.h"
#include "../header/service.h"int main() {setbuf(stdout, NULL);mainMenuDisplay();int code;scanf ("%d", &code);menuJudge (code);return 0;
}

代码包获取连接:

用Clion开发的话那个CMake文件是需要写的,有特定的语法,我不是搞C++的也没有具体去研究,写的时候是直接暴力方法把代码直接写到了cmake-build-debug包里面,这是一种错误的方式,希望面向C++的同学不要模仿。
但如果是面向其他语言的同学仅仅是因为课程关系不得不做这个课程设计的话,为了方便还是直接将代码包的获取连接附上:

链接:https://pan.baidu.com/s/1YV-sVC81_neCfM8jx1xQNQ 提取码:ai4m;

总结:

习惯了JAVA的开发思想和模式,转来用C语言总感觉处处受限,什么地方都很麻烦,但另一方面其实也体现了我对JAVA封装体系的依赖性,比如就一个简简单单的时间比较,在JAVA中有Date类可以直接比较,而C语言中则需要经过一系列麻烦的转换。这绝对不是坏处,至少经过这一次我知道了Date的比较是怎么比较的,想来也就是将C语言那一套封装起来了而已。
另一方面,这是我第一次自己构思一个程序怎么去设计,哪怕只是一个很简单的课程设计,只能说缺陷很多,有很多做的不好的地方,等到写完的时候才发现漏洞百出,这再一次体现了面向对象设计中OOA、OOD的重要性,无关语言,其实思想是一样的哪怕是C语言,我本也应该按照这套思想去做,但显然我没有。

说明:

本篇博客只提供参考,请控制自己不要直接四键一套了事,你既然来搜了说明你已经有了这样的倾向。只要稍微学了一点C语言,按照设计文档的思路去理一遍,过个答辩还是没有问题的。
代码已经测试,按照系统的提示操作没有任何问题,若出现其他技术上的问题请自行解决。


http://chatgpt.dhexx.cn/article/TsIquuDA.shtml

相关文章

基于java学生考勤管理系统设计——计算机毕业设计

考勤作为一个学校的基础管理,是对学生的个人出勤情况的依据。传统的考勤靠手工操作、纸质传递,这样的方式造成了考勤不全面、数据不准确和资料的共享程度低。因此学校需要一个可以适应大量信息控制和数据处理的考勤管理系统,用计算机的高效处理方法和数据库的严谨结构代替手工操…

基于JavaSwing的学生考勤管理系统设计与实现

目录 前言 7 一、系统开发环境及相关技术 8 &#xff08;一&#xff09;系统设计思想及处理流程 8 &#xff08;二&#xff09;运行环境 8 &#xff08;三&#xff09;开发技术及开发工具简介 8 三、需求分析 10 &#xff08;一&#xff09;学生用户需求 10 &#xff08;二&…

《学生考勤信息管理系统》数据库课程设计

目录 一、 需求分析 前台功能模块 后台功能模块 1.1 功能模块的划分及介绍 1.2 实体及重要属性 1.3 业务流程图 二、 概念结构设计 2.1. E-R图的设计 三 、逻辑结构设计 表设计 User1-用户表 Student-学生信息表 College-院系信息表 Attendance personnel 考勤人员表 C…

多个div在同一行显示

使用float:left&#xff0c;也可以使用display : inline-block&#xff0c;可以使多个div在同一行显示。 <div class"search_row"><div class"form-group" style"float:left" > <%-- 通过左浮动使多个div在一行显示--%&g…

HTML如何让两个div并排在一行,如何实现两个或多个div并列于一行

div是块状元素&#xff0c;默认是独占据一行。 但是&#xff0c;如何让两个或多个块区域并列于一行&#xff1f; 块状元素有一个很重要的“float”属性&#xff0c;可以达成这种效果。float 属性也被称为浮动属性&#xff0c;这个词非常形象。 对前面的div元素设置浮动属性后&a…

两个div在一行显示

原因&#xff1a;div为块级元素&#xff0c;默认占一行高度 解决方法1&#xff1a;两个div都添加样式 display&#xff1a;inline-block&#xff1b;&#xff08;如值为inline&#xff0c;设置宽高失效&#xff0c;div靠内容撑起&#xff09; 代码&#xff1a; <style>…

html如何在同一行显示两个div?

代码&#xff1a; <div style"background: blue;float:left">aaa </div> <div style"background: green;float:left">bbb </div>效果&#xff1a;

在HTML中如何让两个图像(div)不换行的显示在一起

代码如下 示意图如下 在两个box后面加入float即可

怎么把两个div一左一右放

怎么把两个div一左一右放 1.代码 <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>Title</title> </head> <body> <div style"width:150px;height:50px;margin:0…

css 并排放置两个div

写代码时遇到了并排放置两个div的需求&#xff0c;于是总结了一下&#xff0c;有以下几种方法可以实现&#xff1a; &#xff08;1&#xff09;设置浮动&#xff1a; a&#xff09;两个div都设置为左浮动&#xff1a; <!DOCTYPE html> <html> <head><…

【HTML】把两个div的内容放置在同一行

使用float属性把两个div的内容放置在同一行&#xff1a; <html> <head> </head> <body><div ><div style"float:right;">我在右侧</div><div>我在左侧</div></div> </body> </html>结果图&…

css如何让两个div并列在同一行

让两个Div并排显示的方法有很多&#xff0c;使用display的inline属性、通过设置float来让Div并排显示都可以实现&#xff0c;感兴趣的朋友可以参考下本文 让两个Div并排显示 一、使用display的inline属性 代码如下 二、通过设置float来让Div并排显示 代码如下 三、对于两个d…

Web前端培训:两个div在同一行可以实现吗?

我们在写页面的时候经常会遇到需要将两个div盒子同行显示的情况&#xff0c;那么“两个Div同行显示”该如何显示呢?一般两个div同行显示可以用float: left和display: inline_block来实现&#xff0c;下面我们分别介绍。 首先我们先来看&#xff0c;没有同行显示的两个div什么…

将两个或多个div放在同一行

1.先画出三个大盒子 2.1在style属性中添加display:inline-block,结果如图&#xff1a; 但是使用该方法盒子与盒子间的默认缝隙是不能修改的&#xff0c;通常我们会使用以下的方法实现两个以上的盒子的摆放 2.2在style属性中添加float&#xff1a;left&#xff1b;将元素移到左…

如何让两个div处于一行

我们知道 div是块级元素,是独占一行的.一般情况下,两个相邻的div是不会处于一行的 例如: Html代码 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>让两个div处于同一行</title&…

1、RH850时钟源及配置

一、时钟源 系统时钟源有5种&#xff1a; 外部主晶振8M-24M。(main osc) 外部副晶振32.768KHz。 &#xff08;sub osc&#xff09; 内部高速晶振8M。(high speed int osc) 内部低速晶振240KHz。(low speed int osc) PLL倍频器。 Note 1. 外部副晶振只有 144 pin and 17…

【经验】RH850 RS-CANFD接收滤波配置

芯片型号&#xff1a;RH850/F1KM-S1 不同PIN封装形式所支持的CAN通道数量不同&#xff0c;RS-CAN通道数量最多可支持6路。 目录 一、FLECCTR&#xff08;接收规则Entry控制寄存器&#xff09; 二、FLCFG0&#xff08;接收规则配置寄存器0&#xff09; 三、FLCFG1&#xff08…

瑞萨RH850 CS+环境下设置堆和栈空间

目录 1、设置栈空间 2、设置堆空间 嵌入式软件开发离不开对堆和栈的操作。 栈由操作系统自动分配和释放 &#xff0c;存放函数的参数值、局部变量的值等。通常都是被调用时处于存储空间中&#xff0c;调用完毕立即释放。 堆由开发者手动分配和释放&#xff0c; 若开发者不释…

基于IAR for RH850的瑞萨RH850 FCL库用法介绍(二)

版权声明 email&#xff1a;1256153255qq.com website for get 瑞萨RH850F1x开发板和瑞萨E1仿真器 认真的朋友会发现&#xff0c;我的上一篇博文《基于IAR for RH850的瑞萨RH850 FCL库用法介绍》在最后留下了一个bug&#xff0c;就是当FCL执行ERASE或WRITE时&#xff0c;返回的…