C语言课程设计:学生考勤系统
项目概述
本项目旨在设计并实现一个基于命令行的学生考勤管理系统,该系统使用C语言进行开发,通过文件存储学生信息和考勤记录,实现了对学生基本信息的增、删、改、查,以及对考勤记录的录入、查询和统计功能,系统采用模块化设计,结构清晰,易于维护和扩展。

(图片来源网络,侵删)
需求分析
根据课程设计的要求,系统需要具备以下核心功能:
-
学生信息管理:
- 添加学生: 输入学号、姓名、班级等信息,将新学生信息存入系统。
- 删除学生: 根据学号删除指定学生的所有信息。
- 修改学生信息: 根据学号查找学生,并修改其姓名、班级等基本信息。
- 查询学生信息: 根据学号或姓名查询并显示学生的详细信息。
- 显示所有学生: 以列表形式展示系统中所有学生的基本信息。
-
考勤记录管理:
- 录入考勤: 为指定学号的学生录入考勤状态(如:正常、迟到、早退、请假、旷课)。
- 查询考勤记录: 查询指定学号学生的所有考勤记录。
- 统计考勤: 统计指定学生或班级的出勤率、迟到次数等。
-
数据持久化:
(图片来源网络,侵删)所有学生信息和考勤记录都应保存在文件中,确保程序关闭后数据不丢失,系统启动时自动从文件加载数据。
系统设计
1 功能模块设计
系统将划分为以下几个主要功能模块,并通过一个主菜单进行统一调度。
+---------------------+
| 主菜单 |
+---------------------+
| 1. 学生信息管理 |
| 2. 考勤记录管理 |
| 3. 退出系统 |
+---------------------+
-
学生信息管理模块:
addStudent()- 添加学生deleteStudent()- 删除学生modifyStudent()- 修改学生信息queryStudent()- 查询学生displayAllStudents()- 显示所有学生
-
考勤记录管理模块:
(图片来源网络,侵删)recordAttendance()- 录入考勤queryAttendance()- 查询考勤记录statisticsAttendance()- 统计考勤
-
数据管理模块:
saveStudentsToFile()- 将学生信息保存到文件loadStudentsFromFile()- 从文件加载学生信息saveAttendanceToFile()- 将考勤记录保存到文件loadAttendanceFromFile()- 从文件加载考勤记录
2 数据结构设计
为了高效地管理数据,我们采用结构体(struct)来定义学生和考勤记录的类型。
// 学生信息结构体
typedef struct {
char id[20]; // 学号
char name[50]; // 姓名
char className[50]; // 班级
} Student;
// 考勤记录结构体
typedef struct {
char studentId[20]; // 学号
char date[20]; // 日期 (格式: YYYY-MM-DD)
char status[20]; // 考勤状态 (正常, 迟到, 早退, 请假, 旷课)
} Attendance;
// 为了方便管理,我们可以使用数组来存储学生和考勤记录
// 在实际项目中,可能会使用链表或数据库,但对于课程设计,数组足够了。
#define MAX_STUDENTS 100
#define MAX_ATTENDANCE 1000
Student students[MAX_STUDENTS];
Attendance attendances[MAX_ATTENDANCE];
int studentCount = 0;
int attendanceCount = 0;
3 文件存储设计
为了实现数据持久化,我们将数据保存在文本文件中。
students.dat: 存储所有学生信息,每行一个学生,字段之间用逗号或制表符分隔。- 格式示例:
2025001,张三,计算机1班
- 格式示例:
attendance.dat: 存储所有考勤记录,每行一条记录,字段之间用逗号或制表符分隔。- 格式示例:
2025001,2025-10-26,正常
- 格式示例:
核心代码实现
以下是关键功能的C语言代码实现。
1 主函数与菜单
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ... (结构体定义和全局变量) ...
void displayMainMenu();
void manageStudents();
void manageAttendance();
int main() {
// 程序启动时,从文件加载数据
loadStudentsFromFile();
loadAttendanceFromFile();
int choice;
do {
displayMainMenu();
printf("请输入您的选择: ");
scanf("%d", &choice);
getchar(); // 清除输入缓冲区中的换行符
switch (choice) {
case 1:
manageStudents();
break;
case 2:
manageAttendance();
break;
case 3:
printf("感谢使用,即将退出系统...\n");
// 退出前保存数据
saveStudentsToFile();
saveAttendanceToFile();
break;
default:
printf("无效的选择,请重新输入!\n");
}
} while (choice != 3);
return 0;
}
void displayMainMenu() {
system("cls || clear"); // 清屏 (Windows: cls, Linux/Mac: clear)
printf("========== 学生考勤系统 ==========\n");
printf(" 1. 学生信息管理\n");
printf(" 2. 考勤记录管理\n");
printf(" 3. 退出系统\n");
printf("==================================\n");
}
2 学生信息管理 - 添加学生
void addStudent() {
if (studentCount >= MAX_STUDENTS) {
printf("学生数量已达上限,无法添加!\n");
return;
}
Student s;
printf("请输入学号: ");
scanf("%s", s.id);
getchar(); // 清除缓冲区
// 检查学号是否已存在
for (int i = 0; i < studentCount; i++) {
if (strcmp(students[i].id, s.id) == 0) {
printf("该学号已存在!\n");
return;
}
}
printf("请输入姓名: ");
scanf("%s", s.name);
getchar();
printf("请输入班级: ");
scanf("%s", s.className);
getchar();
students[studentCount++] = s;
printf("学生信息添加成功!\n");
}
3 学生信息管理 - 查询学生
void queryStudent() {
if (studentCount == 0) {
printf("系统中没有学生信息!\n");
return;
}
int choice;
printf("按学号查询请按 1,按姓名查询请按 2: ");
scanf("%d", &choice);
getchar();
char keyword[50];
printf("请输入查询关键词: ");
scanf("%s", keyword);
getchar();
int found = 0;
printf("\n--- 查询结果 ---\n");
printf("学号\t\t姓名\t\t班级\n");
printf("--------------------------------\n");
for (int i = 0; i < studentCount; i++) {
if ((choice == 1 && strcmp(students[i].id, keyword) == 0) ||
(choice == 2 && strcmp(students[i].name, keyword) == 0)) {
printf("%s\t\t%s\t\t%s\n", students[i].id, students[i].name, students[i].className);
found = 1;
}
}
if (!found) {
printf("未找到匹配的学生信息,\n");
}
printf("----------------\n");
}
4 考勤记录管理 - 录入考勤
void recordAttendance() {
if (studentCount == 0) {
printf("请先添加学生信息!\n");
return;
}
char studentId[20];
printf("请输入要考勤的学生学号: ");
scanf("%s", studentId);
getchar();
// 检查学号是否存在
int studentIndex = -1;
for (int i = 0; i < studentCount; i++) {
if (strcmp(students[i].id, studentId) == 0) {
studentIndex = i;
break;
}
}
if (studentIndex == -1) {
printf("未找到该学号的学生!\n");
return;
}
Attendance a;
strcpy(a.studentId, studentId);
printf("请输入考勤日期 (YYYY-MM-DD): ");
scanf("%s", a.date);
getchar();
printf("请输入考勤状态 (正常/迟到/早退/请假/旷课): ");
scanf("%s", a.status);
getchar();
attendances[attendanceCount++] = a;
printf("考勤记录录入成功!\n");
}
5 文件操作 - 保存与加载
void saveStudentsToFile() {
FILE *fp = fopen("students.dat", "w");
if (fp == NULL) {
printf("无法打开文件 students.dat 进行写入!\n");
return;
}
for (int i = 0; i < studentCount; i++) {
fprintf(fp, "%s,%s,%s\n", students[i].id, students[i].name, students[i].className);
}
fclose(fp);
}
void loadStudentsFromFile() {
FILE *fp = fopen("students.dat", "r");
if (fp == NULL) {
// 文件不存在是正常情况,说明是第一次运行
return;
}
studentCount = 0;
while (fscanf(fp, "%[^,],%[^,],%[^\n]\n", students[studentCount].id, students[studentCount].name, students[studentCount].className) == 3) {
studentCount++;
}
fclose(fp);
}
注意:fscanf的格式%[^,]表示读取直到遇到逗号为止的字符。%[^\n]表示读取直到遇到换行符为止的字符。
6 考勤统计功能
void statisticsAttendance() {
if (attendanceCount == 0) {
printf("没有考勤记录可以统计!\n");
return;
}
char studentId[20];
printf("请输入要统计的学生学号: ");
scanf("%s", studentId);
getchar();
int totalDays = 0;
int normalDays = 0;
int lateDays = 0;
int earlyLeaveDays = 0;
int leaveDays = 0;
int absentDays = 0;
for (int i = 0; i < attendanceCount; i++) {
if (strcmp(attendances[i].studentId, studentId) == 0) {
totalDays++;
if (strcmp(attendances[i].status, "正常") == 0) normalDays++;
else if (strcmp(attendances[i].status, "迟到") == 0) lateDays++;
else if (strcmp(attendances[i].status, "早退") == 0) earlyLeaveDays++;
else if (strcmp(attendances[i].status, "请假") == 0) leaveDays++;
else if (strcmp(attendances[i].status, "旷课") == 0) absentDays++;
}
}
if (totalDays == 0) {
printf("未找到该学生的考勤记录!\n");
return;
}
printf("\n--- 学生 %s 的考勤统计 ---\n", studentId);
printf("总考勤天数: %d\n", totalDays);
printf("正常: %d 天\n", normalDays);
printf("迟到: %d 天\n", lateDays);
printf("早退: %d 天\n", earlyLeaveDays);
printf("请假: %d 天\n", leaveDays);
printf("旷课: %d 天\n", absentDays);
float attendanceRate = (float)(totalDays - absentDays) / totalDays * 100;
printf("出勤率: %.2f%%\n", attendanceRate);
printf("--------------------------\n");
}
功能演示流程
- 启动系统: 运行程序,显示主菜单。
- 添加学生: 选择
1 -> 1,输入学号2025001,姓名张三,班级计算机1班,重复添加2025002, 李四, 计算机1班。 - 查询学生: 选择
1 -> 4,选择按学号查询,输入2025001,应能看到张三的信息。 - 录入考勤: 选择
2 -> 1,为学号2025001录入今天的考勤,状态选择正常,再为2025001录录一条昨天的考勤,状态为迟到。 - 统计考勤: 选择
2 -> 3,输入学号2025001,系统应显示张三的总考勤天数、迟到次数和出勤率。 - 退出系统: 选择
3,程序自动将数据保存到students.dat和attendance.dat文件中。 - 重新启动: 再次运行程序,数据应被自动加载,所有操作结果依然存在。
项目总结与展望
1 总结
本项目成功实现了一个功能完整的学生考勤系统,满足了课程设计的所有要求,系统通过结构化编程思想,将不同功能模块化,提高了代码的可读性和可维护性,文件I/O操作保证了数据的持久化存储,整个系统界面友好,操作流程清晰。
2 不足与展望
- 数据结构局限: 当前使用数组存储数据,数据量受限于数组大小,未来可以改用链表或动态数组来管理,实现数据的动态增长。
- 文件格式: 使用文本文件存储,数据安全性不高,且不易进行复杂查询,未来可以升级为使用数据库(如SQLite)来存储数据,以获得更好的性能、安全性和查询能力。
- 用户界面: 界面为纯命令行,较为简陋,可以引入图形用户界面库(如GTK, Qt)来开发更美观、易用的桌面应用。
- 功能扩展:
- 增加批量导入/导出功能(如从Excel/CSV导入)。
- 增加按班级统计功能。
- 增加权限管理,区分管理员和学生用户。
- 增加数据备份与恢复功能。
这个项目不仅巩固了C语言的基础知识(如结构体、数组、文件操作、函数指针等),也锻炼了分析和解决实际问题的能力,是一个非常好的课程设计实践。
