From 73a396a9d94c001f755d0fe2b10d0d3121ec106b Mon Sep 17 00:00:00 2001 From: LHY0125 <3364451258@qq.com> Date: Sat, 12 Jul 2025 23:33:14 +0800 Subject: [PATCH] Add files via upload --- Makefile | 72 +++++ auxiliary.c | 449 ++++++++++++++++++++++++++++++ auxiliary.h | 62 +++++ config.h | 108 ++++++++ core_handlers.c | 154 +++++++++++ core_handlers.h | 18 ++ globals.c | 25 ++ globals.h | 39 +++ main.c | 146 ++++++++++ main_menu.c | 92 ++++++ main_menu.h | 12 + statistical_analysis.c | 488 ++++++++++++++++++++++++++++++++ statistical_analysis.h | 71 +++++ stu.data.h | 18 ++ stu_data.c | 614 +++++++++++++++++++++++++++++++++++++++++ student_system.exe | Bin 0 -> 100360 bytes student_system_new.exe | Bin 0 -> 100872 bytes test_ranking.txt | 6 + user_manage.c | 265 ++++++++++++++++++ user_manage.h | 13 + 20 files changed, 2652 insertions(+) create mode 100644 Makefile create mode 100644 auxiliary.c create mode 100644 auxiliary.h create mode 100644 config.h create mode 100644 core_handlers.c create mode 100644 core_handlers.h create mode 100644 globals.c create mode 100644 globals.h create mode 100644 main.c create mode 100644 main_menu.c create mode 100644 main_menu.h create mode 100644 statistical_analysis.c create mode 100644 statistical_analysis.h create mode 100644 stu.data.h create mode 100644 stu_data.c create mode 100644 student_system.exe create mode 100644 student_system_new.exe create mode 100644 test_ranking.txt create mode 100644 user_manage.c create mode 100644 user_manage.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9074f0e --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +# 学生成绩管理系统 Makefile +# 编译器设置 +CC = gcc +CFLAGS = -Wall -Wextra -std=c99 -g + +# 目标文件 +TARGET = student_system + +# 源文件 +SOURCES = main.c globals.c auxiliary.c main_menu.c user_manage.c stu_data.c statistical_analysis.c + +# 目标文件 +OBJECTS = $(SOURCES:.c=.o) + +# 头文件 +HEADERS = config.h globals.h auxiliary.h main_menu.h user_manage.h stu.data.h statistical_analysis.h + +# 默认目标 +all: $(TARGET) + +# 链接目标文件 +$(TARGET): $(OBJECTS) + $(CC) $(OBJECTS) -o $(TARGET) + +# 编译源文件 +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c $< -o $@ + +# 清理编译文件 +clean: + rm -f $(OBJECTS) $(TARGET) + rm -f *.exe + +# 创建必要的目录 +setup: + mkdir -p data + mkdir -p backup + +# 运行程序 +run: $(TARGET) + ./$(TARGET) + +# Windows 特定目标 +windows: CFLAGS += -DWINDOWS +windows: $(TARGET) + +# 调试版本 +debug: CFLAGS += -DDEBUG -O0 +debug: $(TARGET) + +# 发布版本 +release: CFLAGS += -O2 -DNDEBUG +release: $(TARGET) + +# 安装(可选) +install: $(TARGET) + cp $(TARGET) /usr/local/bin/ + +# 帮助信息 +help: + @echo "可用的目标:" + @echo " all - 编译程序(默认)" + @echo " clean - 清理编译文件" + @echo " setup - 创建必要的目录" + @echo " run - 编译并运行程序" + @echo " windows - 编译Windows版本" + @echo " debug - 编译调试版本" + @echo " release - 编译发布版本" + @echo " install - 安装程序到系统" + @echo " help - 显示此帮助信息" + +.PHONY: all clean setup run windows debug release install help \ No newline at end of file diff --git a/auxiliary.c b/auxiliary.c new file mode 100644 index 0000000..9166e3f --- /dev/null +++ b/auxiliary.c @@ -0,0 +1,449 @@ +/** + * @file auxiliary.c + * @brief 辅助函数实现文件 + * @note 实现系统中使用的各种辅助函数 + */ + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif + +#include "auxiliary.h" +#include "config.h" +#include "globals.h" +#include "stu.data.h" +#include "user_manage.h" + +/** + * @brief 清理输入缓冲区 + */ +void clearInputBuffer() +{ + int c; + while ((c = getchar()) != '\n' && c != EOF) + ; +} + +/** + * @brief 暂停系统,等待用户按键 + */ +void pauseSystem() +{ + printf("\n按任意键继续..."); +#ifdef _WIN32 + _getch(); +#else + getchar(); +#endif + printf("\n"); +} + +/** + * @brief 清屏 + */ +void clearScreen() +{ +#ifdef _WIN32 + system("cls"); +#else + system("clear"); +#endif +} + +/** + * @brief 打印分隔线 + */ +void printSeparator() +{ + printf("========================================\n"); +} + +/** + * @brief 打印标题头 + * @param title 标题文本 + */ +void printHeader(const char *title) +{ + printSeparator(); + printf(" %s\n", title); + printSeparator(); +} + +/** + * @brief 验证分数是否合法 + * @param score 分数 + * @return 合法返回true,否则返回false + */ +bool isValidScore(float score) +{ + return score >= MIN_SCORE && score <= MAX_SCORE; +} + +/** + * @brief 验证学号格式 + * @param id 学号 + * @return 合法返回true,否则返回false + */ +bool isValidStudentID(const char *id) +{ + if (id == NULL || strlen(id) == 0 || strlen(id) >= MAX_ID_LENGTH) + { + return false; + } + + // 检查是否只包含数字和字母 + for (int i = 0; id[i] != '\0'; i++) + { + if (!isalnum(id[i])) + { + return false; + } + } + + return true; +} + +/** + * @brief 验证姓名格式 + * @param name 姓名 + * @return 合法返回true,否则返回false + */ +bool isValidName(const char *name) +{ + if (name == NULL || strlen(name) == 0 || strlen(name) >= MAX_NAME_LENGTH) + { + return false; + } + + // 检查是否包含非法字符 + for (int i = 0; name[i] != '\0'; i++) + { + if (!isalpha(name[i]) && name[i] != ' ' && name[i] != '-' && + (unsigned char)name[i] < 128) + { // 允许中文字符 + return false; + } + } + + return true; +} + +/** + * @brief 验证性别 + * @param gender 性别字符 + * @return 合法返回true,否则返回false + */ +bool isValidGender(char gender) +{ + return gender == GENDER_MALE || gender == GENDER_FEMALE; +} + +/** + * @brief 验证年龄 + * @param age 年龄 + * @return 合法返回true,否则返回false + */ +bool isValidAge(int age) +{ + return age >= 10 && age <= 100; +} + +/** + * @brief 去除字符串首尾空格 + * @param str 字符串 + */ +void trimString(char *str) +{ + if (str == NULL) + return; + + // 去除开头空格 + char *start = str; + while (isspace(*start)) + start++; + + // 去除结尾空格 + char *end = str + strlen(str) - 1; + while (end > start && isspace(*end)) + end--; + + // 移动字符串 + int len = end - start + 1; + memmove(str, start, len); + str[len] = '\0'; +} + +/** + * @brief 判断字符串是否为空 + * @param str 字符串 + * @return 为空返回true,否则返回false + */ +bool isEmptyString(const char *str) +{ + if (str == NULL) + return true; + + while (*str) + { + if (!isspace(*str)) + return false; + str++; + } + return true; +} + +/** + * @brief 检查文件是否存在 + * @param filename 文件名 + * @return 存在返回true,否则返回false + */ +bool fileExists(const char *filename) +{ + FILE *file = fopen(filename, "r"); + if (file) + { + fclose(file); + return true; + } + return false; +} + +/** + * @brief 创建目录 + * @param path 目录路径 + * @return 成功返回true,否则返回false + */ +bool createDirectory(const char *path) +{ +#ifdef _WIN32 + return _mkdir(path) == 0 || errno == EEXIST; +#else + return mkdir(path, 0755) == 0 || errno == EEXIST; +#endif +} + +/** + * @brief 计算平均分 + * @param scores 分数数组 + * @param count 分数个数 + * @return 平均分 + */ +float calculateAverage(float scores[], int count) +{ + if (count <= 0) + return 0.0; + + float sum = 0.0; + for (int i = 0; i < count; i++) + { + sum += scores[i]; + } + return sum / count; +} + +/** + * @brief 安全输入整数 + * @param prompt 提示信息 + * @param min 最小值 + * @param max 最大值 + * @return 输入的整数 + */ +int safeInputInt(const char *prompt, int min, int max) +{ + int value; + char buffer[100]; + + while (1) + { + printf("%s (%d-%d): ", prompt, min, max); + + if (fgets(buffer, sizeof(buffer), stdin) != NULL) + { + if (sscanf(buffer, "%d", &value) == 1) + { + if (value >= min && value <= max) + { + return value; + } + } + } + + printError("输入无效,请重新输入!"); + } +} + +/** + * @brief 安全输入浮点数 + * @param prompt 提示信息 + * @param min 最小值 + * @param max 最大值 + * @return 输入的浮点数 + */ +float safeInputFloat(const char *prompt, float min, float max) +{ + float value; + char buffer[100]; + + while (1) + { + printf("%s (%.1f-%.1f): ", prompt, min, max); + + if (fgets(buffer, sizeof(buffer), stdin) != NULL) + { + if (sscanf(buffer, "%f", &value) == 1) + { + if (value >= min && value <= max) + { + return value; + } + } + } + + printError("输入无效,请重新输入!"); + } +} + +/** + * @brief 安全输入字符串 + * @param prompt 提示信息 + * @param buffer 缓冲区 + * @param maxLen 最大长度 + */ +void safeInputString(const char *prompt, char *buffer, int maxLen) +{ + while (1) + { + printf("%s: ", prompt); + + if (fgets(buffer, maxLen, stdin) != NULL) + { + // 移除换行符 + buffer[strcspn(buffer, "\n")] = '\0'; + trimString(buffer); + + if (!isEmptyString(buffer)) + { + return; + } + } + + printError("输入不能为空,请重新输入!"); + } +} + +/** + * @brief 彩色输出 + * @param text 文本 + * @param color 颜色代码 + */ +void printColored(const char *text, const char *color) +{ + printf("%s%s%s", color, text, COLOR_RESET); +} + +/** + * @brief 成功消息 + * @param message 消息 + */ +void printSuccess(const char *message) +{ + printColored(message, COLOR_GREEN); + printf("\n"); +} + +/** + * @brief 错误消息 + * @param message 消息 + */ +void printError(const char *message) +{ + printColored(message, COLOR_RED); + printf("\n"); +} + +/** + * @brief 警告消息 + * @param message 消息 + */ +void printWarning(const char *message) +{ + printColored(message, COLOR_YELLOW); + printf("\n"); +} + +/** + * @brief 信息消息 + * @param message 消息 + */ +void printInfo(const char *message) +{ + printColored(message, COLOR_CYAN); + printf("\n"); +} + +/** + * @brief 初始化系统 + * @return 成功返回true,否则返回false + */ +bool initializeSystem() +{ + // 创建数据目录 + if (!createDataDirectories()) + { + return false; + } + + // 加载用户数据 + loadUsersFromFile(); + + // 加载学生数据 + loadStudentsFromFile(); + + systemInitialized = true; + return true; +} + +/** + * @brief 创建数据目录 + * @return 成功返回true,否则返回false + */ +bool createDataDirectories() +{ + if (!createDirectory("data")) + { + return false; + } + + if (!createDirectory(BACKUP_DIR)) + { + return false; + } + + return true; +} + +/** + * @brief 清理系统资源 + */ +void cleanupSystem() +{ + // 保存数据 + if (dataModified) + { + saveStudentsToFile(); + saveUsersToFile(); + } + + systemInitialized = false; +} \ No newline at end of file diff --git a/auxiliary.h b/auxiliary.h new file mode 100644 index 0000000..2a2e5cf --- /dev/null +++ b/auxiliary.h @@ -0,0 +1,62 @@ +/** + * @file auxiliary.h + * @brief 辅助函数头文件 + * @note 包含系统中使用的各种辅助函数声明 + */ + +#ifndef AUXILIARY_H +#define AUXILIARY_H + +#include +#include "main_menu.h" + +// 输入输出辅助函数 +void clearInputBuffer(); // 清理输入缓冲区 +void pauseSystem(); // 暂停系统,等待用户按键 +void clearScreen(); // 清屏 +void printSeparator(); // 打印分隔线 +void printHeader(const char* title); // 打印标题头 + +// 数据验证函数 +bool isValidScore(float score); // 验证分数是否合法 (0-100) +bool isValidStudentID(const char* id); // 验证学号格式 +bool isValidName(const char* name); // 验证姓名格式 +bool isValidGender(char gender); // 验证性别 +bool isValidAge(int age); // 验证年龄 + +// 字符串处理函数 +void trimString(char* str); // 去除字符串首尾空格 +bool isEmptyString(const char* str); // 判断字符串是否为空 +void toLowerCase(char* str); // 转换为小写 +void toUpperCase(char* str); // 转换为大写 + +// 文件操作辅助函数 +bool fileExists(const char* filename); // 检查文件是否存在 +bool createDirectory(const char* path); // 创建目录 +bool backupFile(const char* source, const char* backup); // 备份文件 + +// 数学计算辅助函数 +float calculateAverage(float scores[], int count); // 计算平均分 +float findMaxScore(float scores[], int count); // 找最高分 +float findMinScore(float scores[], int count); // 找最低分 + + +// 安全输入函数 +int safeInputInt(const char* prompt, int min, int max); // 安全输入整数 +float safeInputFloat(const char* prompt, float min, float max); // 安全输入浮点数 +void safeInputString(const char* prompt, char* buffer, int maxLen); // 安全输入字符串 +char safeInputChar(const char* prompt, const char* validChars); // 安全输入字符 + +// 系统初始化和清理函数 +bool initializeSystem(); // 初始化系统 +void cleanupSystem(); // 清理系统资源 +bool createDataDirectories(); // 创建数据目录 + +// 颜色输出函数 +void printColored(const char* text, const char* color); // 彩色输出 +void printSuccess(const char* message); // 成功消息 +void printError(const char* message); // 错误消息 +void printWarning(const char* message); // 警告消息 +void printInfo(const char* message); // 信息消息 + +#endif // AUXILIARY_H \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..d9036d5 --- /dev/null +++ b/config.h @@ -0,0 +1,108 @@ +/** + * @file config.h + * @brief 学生成绩管理系统参数配置头文件 + * @note 本文件集中定义了学生成绩管理系统的所有参数配置,便于统一管理和修改 + */ + +#ifndef CONFIG_H +#define CONFIG_H + +// 系统配置参数 +#define MAX_STUDENTS 1000 // 最大学生数量 +#define MAX_COURSES 10 // 每个学生最多课程数 +#define MAX_USERS 50 // 最大用户数量 +#define MAX_LOGIN_ATTEMPTS 3 // 最大登录尝试次数 + +// 字符串长度限制 +#define MAX_ID_LENGTH 20 // 学号最大长度 +#define MAX_NAME_LENGTH 50 // 姓名最大长度 +#define MAX_COURSE_NAME_LENGTH 50 // 课程名称最大长度 +#define MAX_USERNAME_LENGTH 30 // 用户名最大长度 +#define MAX_PASSWORD_LENGTH 30 // 密码最大长度 + +// 分数相关配置 +#define MIN_SCORE 0.0 // 最低分数 +#define MAX_SCORE 100.0 // 最高分数 +#define PASS_SCORE 60.0 // 及格分数 +#define EXCELLENT_SCORE 90.0 // 优秀分数 + +// 文件路径配置 +#define STUDENTS_FILE "data/students.csv" // 学生数据文件 +#define USERS_FILE "data/users.txt" // 用户数据文件 +#define BACKUP_DIR "backup/" // 备份目录 + +// 菜单选项定义 +#define MENU_EXIT 0 +#define MENU_BASIC_FUNCTIONS 1 +#define MENU_STATISTICS 2 +#define MENU_ADMIN 3 + +// 基本功能菜单选项 +#define BASIC_BACK 0 +#define BASIC_ADD_STUDENT 1 +#define BASIC_DELETE_STUDENT 2 +#define BASIC_MODIFY_STUDENT 3 +#define BASIC_SEARCH_BY_ID 4 +#define BASIC_SEARCH_BY_NAME 5 +#define BASIC_DISPLAY_ALL 6 +#define BASIC_SORT_STUDENTS 7 + +// 统计功能菜单选项 +#define STATS_BACK 0 +#define STATS_COURSE_ANALYSIS 1 +#define STATS_SCORE_DISTRIBUTION 2 +#define STATS_SCORE_RANGES 3 +#define STATS_OVERALL_ANALYSIS 4 + +// 管理功能菜单选项 +#define ADMIN_BACK 0 +#define ADMIN_ADD_USER 1 +#define ADMIN_DELETE_USER 2 +#define ADMIN_MODIFY_PASSWORD 3 +#define ADMIN_VIEW_USERS 4 + +// 排序选项 +#define SORT_BY_ID 1 +#define SORT_BY_NAME 2 +#define SORT_BY_TOTAL_SCORE 3 +#define SORT_BY_AVERAGE_SCORE 4 + +// 排序顺序 +#define SORT_ASCENDING 1 +#define SORT_DESCENDING 2 + +// 性别定义 +#define GENDER_MALE 'M' +#define GENDER_FEMALE 'F' + +// 颜色代码(用于美化输出) +#define COLOR_RESET "\033[0m" +#define COLOR_RED "\033[31m" +#define COLOR_GREEN "\033[32m" +#define COLOR_YELLOW "\033[33m" +#define COLOR_BLUE "\033[34m" +#define COLOR_MAGENTA "\033[35m" +#define COLOR_CYAN "\033[36m" +#define COLOR_WHITE "\033[37m" + +// 系统消息 +#define MSG_SUCCESS "操作成功!" +#define MSG_FAILURE "操作失败!" +#define MSG_NOT_FOUND "未找到相关记录!" +#define MSG_INVALID_INPUT "输入无效,请重新输入!" +#define MSG_FILE_ERROR "文件操作错误!" + +// 数据结构定义 +typedef struct { + char studentID[MAX_ID_LENGTH]; // 学号 + char name[MAX_NAME_LENGTH]; // 姓名 + int age; // 年龄 + char gender; // 性别 ('M'/'F') + char courses[MAX_COURSES][MAX_COURSE_NAME_LENGTH]; // 课程名称 + float scores[MAX_COURSES]; // 各科成绩 + int courseCount; // 课程数量 + float totalScore; // 总分 + float averageScore; // 平均分 +} Student; + +#endif // CONFIG_H \ No newline at end of file diff --git a/core_handlers.c b/core_handlers.c new file mode 100644 index 0000000..4f4ab5d --- /dev/null +++ b/core_handlers.c @@ -0,0 +1,154 @@ +/** + * @file core_handlers.c + * @brief 核心处理函数实现文件 + * @note 实现主要的功能处理函数 + */ + + #include + #include + #include + + #include "core_handlers.h" + #include "config.h" + #include "globals.h" + #include "main_menu.h" + #include "user_manage.h" + #include "stu.data.h" + #include "statistical_analysis.h" + #include "auxiliary.h" + + /** + * @brief 处理基本功能菜单 + */ + void handleBasicFunctions() + { + int choice; + do { + clearScreen(); + displayBasicFunctionsMenu(); + choice = safeInputInt("请选择功能", BASIC_BACK, BASIC_SORT_STUDENTS); + + switch (choice) { + case BASIC_ADD_STUDENT: + addStudent(); + break; + case BASIC_DELETE_STUDENT: + deleteStudent(); + break; + case BASIC_MODIFY_STUDENT: + modifyStudent(); + break; + case BASIC_SEARCH_BY_ID: + searchStudentByID(); + break; + case BASIC_SEARCH_BY_NAME: + searchStudentByName(); + break; + case BASIC_DISPLAY_ALL: + displayAllStudents(); + break; + case BASIC_SORT_STUDENTS: + handleSortStudents(); + break; + case BASIC_BACK: + break; + default: + printError("无效的选择!"); + pauseSystem(); + break; + } + } while (choice != BASIC_BACK); + } + + /** + * @brief 处理统计功能菜单 + */ + void handleStatistics() { + int choice; + do { + clearScreen(); + displayStatisticsMenu(); + choice = safeInputInt("请选择功能", STATS_BACK, STATS_OVERALL_ANALYSIS); + + switch (choice) { + case STATS_COURSE_ANALYSIS: + displayCourseStatistics(); + break; + case STATS_SCORE_DISTRIBUTION: + displayScoreDistribution(); + break; + case STATS_SCORE_RANGES: + displayStudentRanking(); + break; + case STATS_OVERALL_ANALYSIS: + displayOverallStatistics(); + break; + case STATS_BACK: + break; + default: + printError("无效的选择!"); + pauseSystem(); + break; + } + } while (choice != STATS_BACK); + } + + /** + * @brief 处理管理功能菜单 + */ + void handleAdminFunctions() { + int choice; + do { + clearScreen(); + displayAdminMenu(); + choice = safeInputInt("请选择功能", ADMIN_BACK, ADMIN_VIEW_USERS); + + switch (choice) { + case ADMIN_ADD_USER: + addUserAccount(); + break; + case ADMIN_DELETE_USER: + deleteUserAccount(); + break; + case ADMIN_MODIFY_PASSWORD: + modifyUserPassword(); + break; + case ADMIN_VIEW_USERS: + viewAllUsers(); + break; + case ADMIN_BACK: + break; + default: + printError("无效的选择!"); + pauseSystem(); + break; + } + } while (choice != ADMIN_BACK); + } + + /** + * @brief 处理学生排序功能 + */ + void handleSortStudents() { + clearScreen(); + printHeader("学生排序"); + + printf("排序依据:\n"); + printf("1. 按学号排序\n"); + printf("2. 按姓名排序\n"); + printf("3. 按总分排序\n"); + printf("4. 按平均分排序\n"); + + int criteria = safeInputInt("请选择排序依据", SORT_BY_ID, SORT_BY_AVERAGE_SCORE); + + printf("\n排序顺序:\n"); + printf("1. 升序\n"); + printf("2. 降序\n"); + + int order = safeInputInt("请选择排序顺序", SORT_ASCENDING, SORT_DESCENDING); + + sortStudents(criteria, order); + + printf("\n排序完成!\n"); + displayAllStudents(); + } \ No newline at end of file diff --git a/core_handlers.h b/core_handlers.h new file mode 100644 index 0000000..c5e7118 --- /dev/null +++ b/core_handlers.h @@ -0,0 +1,18 @@ +/** + * @file core_handlers.h + * @brief 核心处理函数头文件 + * @note 包含主要的功能处理函数声明 + */ + +#ifndef CORE_HANDLERS_H +#define CORE_HANDLERS_H + +#include "config.h" + +// 核心处理函数声明 +void handleBasicFunctions(); // 处理基本功能菜单 +void handleStatistics(); // 处理统计功能菜单 +void handleAdminFunctions(); // 处理管理功能菜单 +void handleSortStudents(); // 处理学生排序功能 + +#endif // CORE_HANDLERS_H \ No newline at end of file diff --git a/globals.c b/globals.c new file mode 100644 index 0000000..f355d04 --- /dev/null +++ b/globals.c @@ -0,0 +1,25 @@ +/** + * @file globals.c + * @brief 全局变量定义文件 + * @note 定义所有全局变量的实际存储空间 + */ + +#include "globals.h" + +// 全局变量定义 +Student students[MAX_STUDENTS]; // 学生数组 +User users[MAX_USERS]; // 用户数组 +int studentCount = 0; // 当前学生数量 +int userCount = 0; // 当前用户数量 +char currentUser[MAX_USERNAME_LENGTH] = ""; // 当前登录用户 +bool isCurrentUserAdmin = false; // 当前用户是否为管理员 + +// 系统状态变量 +bool systemInitialized = false; // 系统是否已初始化 +bool dataModified = false; // 数据是否已修改 + +// 统计信息缓存 +float overallAverageScore = 0.0; // 全体学生平均分 +float highestScore = 0.0; // 最高分 +float lowestScore = 100.0; // 最低分 +bool statsNeedUpdate = true; // 统计信息是否需要更新 \ No newline at end of file diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..9f36f8f --- /dev/null +++ b/globals.h @@ -0,0 +1,39 @@ +/** + * @file globals.h + * @brief 全局变量声明头文件 + * @note 集中管理所有全局变量的声明,提高代码可维护性 + */ + +#ifndef GLOBALS_H +#define GLOBALS_H + +#include +#include "config.h" +#include "main_menu.h" + +// 用户结构体定义 +typedef struct { + char username[MAX_USERNAME_LENGTH]; + char password[MAX_PASSWORD_LENGTH]; + bool isAdmin; // 是否为管理员 +} User; + +// 全局变量声明 +extern Student students[MAX_STUDENTS]; // 学生数组 +extern User users[MAX_USERS]; // 用户数组 +extern int studentCount; // 当前学生数量 +extern int userCount; // 当前用户数量 +extern char currentUser[MAX_USERNAME_LENGTH]; // 当前登录用户 +extern bool isCurrentUserAdmin; // 当前用户是否为管理员 + +// 系统状态变量 +extern bool systemInitialized; // 系统是否已初始化 +extern bool dataModified; // 数据是否已修改(用于判断是否需要保存) + +// 统计信息缓存(可选,用于提高性能) +extern float overallAverageScore; // 全体学生平均分 +extern float highestScore; // 最高分 +extern float lowestScore; // 最低分 +extern bool statsNeedUpdate; // 统计信息是否需要更新 + +#endif // GLOBALS_H \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..6d004a7 --- /dev/null +++ b/main.c @@ -0,0 +1,146 @@ +/** + * @file main.c + * @brief 学生成绩管理系统主程序 + * @note 系统入口点,包含主要的程序流程控制 + */ + +#include +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +#include "config.h" +#include "globals.h" +#include "main_menu.h" +#include "user_manage.h" +#include "stu.data.h" +#include "statistical_analysis.h" +#include "auxiliary.h" +#include "core_handlers.h" + +/** + * @brief 主程序入口 + * @param argc 命令行参数个数 + * @param argv 命令行参数数组 + * @return 程序退出状态码 + * @note 系统中有两个用户: + 1. admin - 密码是 123456 + 2. teacher - 密码是 password + * @note + gcc -o student_system.exe main.c stu_data.c auxiliary.c statistical_analysis.c main_menu.c globals.c user_manage.c core_handlers.c + .\student_system.exe + */ +int main(int argc, char *argv[]) +{ + // 设置控制台编码为UTF-8 +#ifdef _WIN32 + system("chcp 65001 > nul"); // 设置控制台编码为UTF-8 + SetConsoleOutputCP(65001); // 设置控制台输出编码 + SetConsoleCP(65001); // 设置控制台输入编码 +#endif + + // 初始化系统 + if (!initializeSystem()) + { + printError("系统初始化失败!"); + return -1; + } + + // 显示欢迎信息 + clearScreen(); + printHeader("学生成绩管理系统"); + printf("\n"); + printInfo("欢迎使用学生成绩管理系统!"); + printf("\n"); + pauseSystem(); + + // 用户登录 + int loginAttempts = 0; + while (loginAttempts < MAX_LOGIN_ATTEMPTS) + { + clearScreen(); + printHeader("用户登录"); + + if (loginSystem()) + { + printSuccess("登录成功!"); + printf("欢迎您,%s%s\n", currentUser, + isCurrentUserAdmin ? " (管理员)" : " (普通用户)"); + pauseSystem(); + break; + } + else + { + loginAttempts++; + if (loginAttempts < MAX_LOGIN_ATTEMPTS) + { + printError("登录失败!请检查用户名和密码。"); + printf("剩余尝试次数:%d\n", MAX_LOGIN_ATTEMPTS - loginAttempts); + pauseSystem(); + } + } + } + + // 登录失败次数过多,退出程序 + if (loginAttempts >= MAX_LOGIN_ATTEMPTS) + { + printError("登录失败次数过多,程序将退出!"); + cleanupSystem(); + return -1; + } + + // 主程序循环 + int choice; + do + { + clearScreen(); + displayMainMenu(); + choice = safeInputInt("请选择功能", MENU_EXIT, MENU_ADMIN); + + switch (choice) + { + case MENU_BASIC_FUNCTIONS: + handleBasicFunctions(); + break; + case MENU_STATISTICS: + handleStatistics(); + break; + case MENU_ADMIN: + if (isCurrentUserAdmin) + { + handleAdminFunctions(); + } + else + { + printError("您没有管理员权限!"); + pauseSystem(); + } + break; + case MENU_EXIT: + // 保存数据并退出 + if (dataModified) + { + printInfo("正在保存数据..."); + saveStudentsToFile(); + saveUsersToFile(); + printSuccess("数据保存完成!"); + } + printInfo("感谢使用学生成绩管理系统!"); + break; + default: + printError("无效的选择!"); + pauseSystem(); + break; + } + } while (choice != MENU_EXIT); + + // 清理系统资源 + cleanupSystem(); + return 0; +} \ No newline at end of file diff --git a/main_menu.c b/main_menu.c new file mode 100644 index 0000000..19dac7d --- /dev/null +++ b/main_menu.c @@ -0,0 +1,92 @@ +/** + * @file main_menu.c + * @brief 主菜单实现文件 + * @note 实现各种菜单显示功能 + */ + + #include + #include "main_menu.h" + #include "config.h" + #include "auxiliary.h" + #include "globals.h" + + /** + * @brief 显示主菜单 + */ + void displayMainMenu() + { + printHeader("学生成绩管理系统 - 主菜单"); + printf("\n"); + printf("当前用户: %s %s\n", currentUser, + isCurrentUserAdmin ? "(管理员)" : "(普通用户)"); + printf("\n"); + printf("1. 基本功能管理\n"); + printf("2. 统计分析功能\n"); + if (isCurrentUserAdmin) + { + printf("3. 系统管理功能\n"); + } + printf("0. 退出系统\n"); + printf("\n"); + printSeparator(); + } + + /** + * @brief 显示基本功能菜单 + */ + void displayBasicFunctionsMenu() + { + printHeader("基本功能管理"); + printf("\n"); + printf("1. 添加学生信息\n"); + printf("2. 删除学生信息\n"); + printf("3. 修改学生信息\n"); + printf("4. 按学号查找学生\n"); + printf("5. 按姓名查找学生\n"); + printf("6. 显示所有学生\n"); + printf("7. 学生信息排序\n"); + printf("0. 返回主菜单\n"); + printf("\n"); + printf("当前学生总数: %d\n", studentCount); + printf("\n"); + printSeparator(); + } + + /** + * @brief 显示统计功能菜单 + */ + void displayStatisticsMenu() + { + printHeader("统计分析功能"); + printf("\n"); + printf("1. 课程统计分析\n"); + printf("2. 成绩分布统计\n"); + printf("3. 分数段统计\n"); + printf("4. 综合统计分析\n"); + printf("0. 返回主菜单\n"); + printf("\n"); + printf("当前学生总数: %d\n", studentCount); + if (studentCount > 0) { + printf("系统平均分: %.2f\n", overallAverageScore); + } + printf("\n"); + printSeparator(); + } + + /** + * @brief 显示管理功能菜单 + */ + void displayAdminMenu() + { + printHeader("系统管理功能"); + printf("\n"); + printf("1. 添加用户账户\n"); + printf("2. 删除用户账户\n"); + printf("3. 修改用户密码\n"); + printf("4. 查看所有用户\n"); + printf("0. 返回主菜单\n"); + printf("\n"); + printf("当前用户总数: %d\n", userCount); + printf("\n"); + printSeparator(); + } \ No newline at end of file diff --git a/main_menu.h b/main_menu.h new file mode 100644 index 0000000..9eff268 --- /dev/null +++ b/main_menu.h @@ -0,0 +1,12 @@ +#ifndef MAIN_MENU_H +#define MAIN_MENU_H + +#include "config.h" + +// 主菜单和子菜单显示函数 +void displayMainMenu(); // 显示主菜单 +void displayBasicFunctionsMenu(); // 显示基本功能菜单 +void displayStatisticsMenu(); // 显示统计功能菜单 +void displayAdminMenu(); // 显示管理功能菜单 + +#endif // MAIN_MENU_H \ No newline at end of file diff --git a/statistical_analysis.c b/statistical_analysis.c new file mode 100644 index 0000000..8a134d1 --- /dev/null +++ b/statistical_analysis.c @@ -0,0 +1,488 @@ +/** + * @file statistical_analysis.c + * @brief 统计分析功能实现文件 + * @note 实现各种统计分析功能 + */ + + #include + #include + #include + #include + #include "statistical_analysis.h" + #include "config.h" + #include "globals.h" + #include "auxiliary.h" + #include "stu.data.h" + + /** + * @brief 显示课程统计信息 + */ + void displayCourseStatistics() + { + clearScreen(); + printHeader("课程统计信息"); + + if (studentCount == 0) + { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + // 收集所有课程名称 + char courses[MAX_STUDENTS * MAX_COURSES][MAX_COURSE_NAME_LENGTH]; + int courseCount = 0; + + for (int i = 0; i < studentCount; i++) { + for (int j = 0; j < students[i].courseCount; j++) { + bool exists = false; + for (int k = 0; k < courseCount; k++) { + if (strcmp(courses[k], students[i].courses[j]) == 0) { + exists = true; + break; + } + } + if (!exists) { + strcpy(courses[courseCount], students[i].courses[j]); + courseCount++; + } + } + } + + if (courseCount == 0) { + printWarning("暂无课程数据!"); + pauseSystem(); + return; + } + + printf("\n"); + printf("%-20s %-8s %-8s %-8s %-8s %-8s\n", + "课程名称", "人数", "最高分", "最低分", "平均分", "及格率"); + printSeparator(); + + for (int i = 0; i < courseCount; i++) { + CourseStats stats = calculateCourseStats(courses[i]); + printf("%-20s %-8d %-8.2f %-8.2f %-8.2f %-8.2f%%\n", + courses[i], stats.studentCount, stats.maxScore, + stats.minScore, stats.averageScore, stats.passRate); + } + + pauseSystem(); + } + + /** + * @brief 计算课程统计信息 + */ + CourseStats calculateCourseStats(const char* courseName) { + CourseStats stats = {0}; + float scores[MAX_STUDENTS]; + int count = 0; + + // 收集该课程的所有分数 + for (int i = 0; i < studentCount; i++) { + for (int j = 0; j < students[i].courseCount; j++) { + if (strcmp(students[i].courses[j], courseName) == 0) { + scores[count] = students[i].scores[j]; + count++; + break; + } + } + } + + if (count == 0) return stats; + + stats.studentCount = count; + stats.maxScore = scores[0]; + stats.minScore = scores[0]; + stats.totalScore = 0; + + int passCount = 0; + + for (int i = 0; i < count; i++) { + if (scores[i] > stats.maxScore) stats.maxScore = scores[i]; + if (scores[i] < stats.minScore) stats.minScore = scores[i]; + stats.totalScore += scores[i]; + + if (scores[i] >= PASS_SCORE) passCount++; + } + + stats.averageScore = stats.totalScore / count; + stats.passRate = (float)passCount / count * 100; + + return stats; + } + + /** + * @brief 显示分数分布 + */ + void displayScoreDistribution() { + clearScreen(); + printHeader("分数分布统计"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + ScoreDistribution dist = calculateScoreDistribution(); + + printf("\n分数段分布:\n"); + printSeparator(); + printf("90-100分: %d人 (%.2f%%)\n", dist.excellent, + (float)dist.excellent / studentCount * 100); + printf("80-89分: %d人 (%.2f%%)\n", dist.good, + (float)dist.good / studentCount * 100); + printf("70-79分: %d人 (%.2f%%)\n", dist.medium, + (float)dist.medium / studentCount * 100); + printf("60-69分: %d人 (%.2f%%)\n", dist.pass, + (float)dist.pass / studentCount * 100); + printf("0-59分: %d人 (%.2f%%)\n", dist.fail, + (float)dist.fail / studentCount * 100); + + printf("\n总体统计:\n"); + printf("总人数: %d\n", studentCount); + printf("及格人数: %d (%.2f%%)\n", + studentCount - dist.fail, + (float)(studentCount - dist.fail) / studentCount * 100); + printf("不及格人数: %d (%.2f%%)\n", + dist.fail, (float)dist.fail / studentCount * 100); + + pauseSystem(); + } + + /** + * @brief 计算分数分布 + */ + ScoreDistribution calculateScoreDistribution() { + ScoreDistribution dist = {0}; + + for (int i = 0; i < studentCount; i++) { + float avgScore = students[i].averageScore; + + if (avgScore >= 90) { + dist.excellent++; + } else if (avgScore >= 80) { + dist.good++; + } else if (avgScore >= 70) { + dist.medium++; + } else if (avgScore >= 60) { + dist.pass++; + } else { + dist.fail++; + } + } + + return dist; + } + + /** + * @brief 显示学生排名 + */ + void displayStudentRanking() { + clearScreen(); + printHeader("学生排名"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + // 创建排名数组 + StudentRank rankings[MAX_STUDENTS]; + for (int i = 0; i < studentCount; i++) { + rankings[i].studentIndex = i; + rankings[i].averageScore = students[i].averageScore; + rankings[i].totalScore = students[i].totalScore; + } + + // 按平均分排序(降序) + for (int i = 0; i < studentCount - 1; i++) { + for (int j = 0; j < studentCount - 1 - i; j++) { + if (rankings[j].averageScore < rankings[j + 1].averageScore) { + StudentRank temp = rankings[j]; + rankings[j] = rankings[j + 1]; + rankings[j + 1] = temp; + } + } + } + + printf("\n"); + // 调整中文表头的对齐格式,考虑中文字符的显示宽度 + printf("%-5s %-10s %-12s %-8s %-8s\n", + "排名", "学号", "姓名", "总分", "平均分"); + printf("========================================\n"); + + for (int i = 0; i < studentCount; i++) { + int idx = rankings[i].studentIndex; + printf("%-5d %-10s %-12s %-8.2f %-8.2f\n", + i + 1, + students[idx].studentID, + students[idx].name, + students[idx].totalScore, + students[idx].averageScore); + } + + pauseSystem(); + } + + /** + * @brief 显示系统总体统计 + */ + void displayOverallStatistics() { + clearScreen(); + printHeader("系统总体统计"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + OverallStats stats = calculateOverallStats(); + + printf("\n学生信息统计:\n"); + printSeparator(); + printf("总学生数: %d\n", stats.totalStudents); + printf("男学生数: %d (%.2f%%)\n", stats.maleCount, + (float)stats.maleCount / stats.totalStudents * 100); + printf("女学生数: %d (%.2f%%)\n", stats.femaleCount, + (float)stats.femaleCount / stats.totalStudents * 100); + printf("平均年龄: %.1f岁\n", stats.averageAge); + + printf("\n成绩统计:\n"); + printSeparator(); + printf("最高平均分: %.2f\n", stats.highestAverage); + printf("最低平均分: %.2f\n", stats.lowestAverage); + printf("全体平均分: %.2f\n", stats.overallAverageScore); + printf("标准差: %.2f\n", stats.standardDeviation); + + printf("\n课程统计:\n"); + printSeparator(); + printf("总课程数: %d\n", stats.totalCourses); + printf("平均课程数/人: %.1f\n", stats.averageCoursesPerStudent); + + pauseSystem(); + } + + /** + * @brief 计算系统总体统计 + */ + OverallStats calculateOverallStats() { + OverallStats stats = {0}; + + if (studentCount == 0) return stats; + + stats.totalStudents = studentCount; + + float totalAge = 0; + float totalAverage = 0; + int totalCourseCount = 0; + + // 收集所有课程名称 + char courses[MAX_STUDENTS * MAX_COURSES][MAX_COURSE_NAME_LENGTH]; + int uniqueCourseCount = 0; + + stats.highestAverage = students[0].averageScore; + stats.lowestAverage = students[0].averageScore; + + for (int i = 0; i < studentCount; i++) { + // 性别统计 + if (students[i].gender == GENDER_MALE) { + stats.maleCount++; + } else if (students[i].gender == GENDER_FEMALE) { + stats.femaleCount++; + } + + // 年龄统计 + totalAge += students[i].age; + + // 成绩统计 + totalAverage += students[i].averageScore; + totalCourseCount += students[i].courseCount; + + if (students[i].averageScore > stats.highestAverage) { + stats.highestAverage = students[i].averageScore; + } + if (students[i].averageScore < stats.lowestAverage) { + stats.lowestAverage = students[i].averageScore; + } + + // 课程统计 + for (int j = 0; j < students[i].courseCount; j++) { + bool exists = false; + for (int k = 0; k < uniqueCourseCount; k++) { + if (strcmp(courses[k], students[i].courses[j]) == 0) { + exists = true; + break; + } + } + if (!exists) { + strcpy(courses[uniqueCourseCount], students[i].courses[j]); + uniqueCourseCount++; + } + } + } + + stats.averageAge = totalAge / studentCount; + stats.overallAverageScore = totalAverage / studentCount; + stats.totalCourses = uniqueCourseCount; + stats.averageCoursesPerStudent = (float)totalCourseCount / studentCount; + + // 计算标准差 + float variance = 0; + for (int i = 0; i < studentCount; i++) { + float diff = students[i].averageScore - stats.overallAverageScore; + variance += diff * diff; + } + variance /= studentCount; + stats.standardDeviation = sqrt(variance); + + return stats; + } + + /** + * @brief 查找最高分学生 + */ + void findTopStudent() { + clearScreen(); + printHeader("最高分学生"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + int topIndex = 0; + for (int i = 1; i < studentCount; i++) { + if (students[i].averageScore > students[topIndex].averageScore) { + topIndex = i; + } + } + + printf("\n最高平均分学生:\n"); + displayStudentInfo(&students[topIndex]); + + pauseSystem(); + } + + /** + * @brief 查找最低分学生 + */ + void findBottomStudent() { + clearScreen(); + printHeader("最低分学生"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + int bottomIndex = 0; + for (int i = 1; i < studentCount; i++) { + if (students[i].averageScore < students[bottomIndex].averageScore) { + bottomIndex = i; + } + } + + printf("\n最低平均分学生:\n"); + displayStudentInfo(&students[bottomIndex]); + + pauseSystem(); + } + + /** + * @brief 按课程查找最高分 + */ + void findTopScoreInCourse() { + clearScreen(); + printHeader("课程最高分查询"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + char courseName[MAX_COURSE_NAME_LENGTH]; + printf("\n"); + safeInputString("请输入课程名称", courseName, MAX_COURSE_NAME_LENGTH); + + float maxScore = -1; + int maxIndex = -1; + + for (int i = 0; i < studentCount; i++) { + for (int j = 0; j < students[i].courseCount; j++) { + if (strcmp(students[i].courses[j], courseName) == 0) { + if (students[i].scores[j] > maxScore) { + maxScore = students[i].scores[j]; + maxIndex = i; + } + } + } + } + + if (maxIndex == -1) { + printError("未找到该课程!"); + } else { + printf("\n课程 \"%s\" 最高分:\n", courseName); + printSeparator(); + printf("学号: %s\n", students[maxIndex].studentID); + printf("姓名: %s\n", students[maxIndex].name); + printf("分数: %.2f\n", maxScore); + } + + pauseSystem(); + } + + /** + * @brief 计算学生统计信息 + */ + void calculateStudentStats(Student* student) { + if (student->courseCount == 0) { + student->totalScore = 0; + student->averageScore = 0; + return; + } + + student->totalScore = 0; + for (int i = 0; i < student->courseCount; i++) { + student->totalScore += student->scores[i]; + } + + student->averageScore = student->totalScore / student->courseCount; + } + + /** + * @brief 更新全局统计缓存 + */ + void updateGlobalStats() { + if (studentCount == 0) { + overallAverageScore = 0; + highestScore = 0; + lowestScore = 0; + statsNeedUpdate = false; + return; + } + + float total = 0; + highestScore = students[0].averageScore; + lowestScore = students[0].averageScore; + + for (int i = 0; i < studentCount; i++) { + total += students[i].averageScore; + + if (students[i].averageScore > highestScore) { + highestScore = students[i].averageScore; + } + if (students[i].averageScore < lowestScore) { + lowestScore = students[i].averageScore; + } + } + + overallAverageScore = total / studentCount; + statsNeedUpdate = false; + } \ No newline at end of file diff --git a/statistical_analysis.h b/statistical_analysis.h new file mode 100644 index 0000000..b34b9eb --- /dev/null +++ b/statistical_analysis.h @@ -0,0 +1,71 @@ +/** + * @file statistical_analysis.h + * @brief 统计分析功能头文件 + * @note 包含各种统计分析功能的函数声明 + */ + +#ifndef STATISTICAL_ANALYSIS_H +#define STATISTICAL_ANALYSIS_H + +#include "config.h" +#include "main_menu.h" + +// 课程统计结构体 +typedef struct { + int studentCount; + float maxScore; + float minScore; + float totalScore; + float averageScore; + float passRate; +} CourseStats; + +// 分数分布结构体 +typedef struct { + int excellent; // 90-100分 + int good; // 80-89分 + int medium; // 70-79分 + int pass; // 60-69分 + int fail; // 0-59分 +} ScoreDistribution; + +// 学生排名结构体 +typedef struct { + int studentIndex; + float averageScore; + float totalScore; +} StudentRank; + +// 总体统计结构体 +typedef struct { + int totalStudents; + int maleCount; + int femaleCount; + float averageAge; + float highestAverage; + float lowestAverage; + float overallAverageScore; + float standardDeviation; + int totalCourses; + float averageCoursesPerStudent; +} OverallStats; + +// 主要统计分析函数 +void displayCourseStatistics(); // 显示课程统计信息 +void displayScoreDistribution(); // 显示分数分布 +void displayStudentRanking(); // 显示学生排名 +void displayOverallStatistics(); // 显示系统总体统计 + +// 查找功能 +void findTopStudent(); // 查找最高分学生 +void findBottomStudent(); // 查找最低分学生 +void findTopScoreInCourse(); // 按课程查找最高分 + +// 计算函数 +CourseStats calculateCourseStats(const char* courseName); +ScoreDistribution calculateScoreDistribution(); +OverallStats calculateOverallStats(); +void calculateStudentStats(Student* student); +void updateGlobalStats(); + +#endif // STATISTICAL_ANALYSIS_H \ No newline at end of file diff --git a/stu.data.h b/stu.data.h new file mode 100644 index 0000000..d4c96fa --- /dev/null +++ b/stu.data.h @@ -0,0 +1,18 @@ +#ifndef STU_DATA_H +#define STU_DATA_H + +#include "config.h" + +// 学生数据管理相关函数 (CRUD) +void loadStudentsFromFile(); // 从文件加载学生数据 +void saveStudentsToFile(); // 将学生数据保存到文件 +void addStudent(); // 增加学生信息 +void deleteStudent(); // 删除学生信息 +void modifyStudent(); // 修改学生信息 +void searchStudentByID(); // 按学号查找学生 +void searchStudentByName(); // 按姓名查找学生 +void displayAllStudents(); // 显示所有学生信息 +void displayStudentInfo(const Student* student); // 显示单个学生信息 +void sortStudents(int criteria, int order); // 统一排序函数,根据参数选择排序依据和升降序 + +#endif // STU_DATA_H \ No newline at end of file diff --git a/stu_data.c b/stu_data.c new file mode 100644 index 0000000..1b8ffd2 --- /dev/null +++ b/stu_data.c @@ -0,0 +1,614 @@ +/** + * @file stu_data.c + * @brief 学生数据管理实现文件 + * @note 实现学生信息的增删改查功能 + */ + +#include +#include +#include +#include "stu.data.h" +#include "config.h" +#include "globals.h" +#include "auxiliary.h" +#include "statistical_analysis.h" + +/** + * @brief 从CSV文件加载学生数据 + */ +void loadStudentsFromFile() { + FILE* file = fopen(STUDENTS_FILE, "r"); + if (file == NULL) { + studentCount = 0; + printInfo("学生数据文件不存在,将创建新文件。"); + return; + } + + char line[1024]; + studentCount = 0; + + // 跳过CSV头部 + if (fgets(line, sizeof(line), file) == NULL) { + fclose(file); + studentCount = 0; + return; + } + + // 读取学生数据 + while (fgets(line, sizeof(line), file) != NULL && studentCount < MAX_STUDENTS) { + Student* student = &students[studentCount]; + memset(student, 0, sizeof(Student)); + + // 解析CSV行 + char* token = strtok(line, ","); + if (token == NULL) continue; + + // 学号 + strncpy(student->studentID, token, MAX_ID_LENGTH - 1); + + // 姓名 + token = strtok(NULL, ","); + if (token == NULL) continue; + strncpy(student->name, token, MAX_NAME_LENGTH - 1); + + // 年龄 + token = strtok(NULL, ","); + if (token == NULL) continue; + student->age = atoi(token); + + // 性别 + token = strtok(NULL, ","); + if (token == NULL) continue; + student->gender = token[0]; + + // 课程数量 + token = strtok(NULL, ","); + if (token == NULL) continue; + student->courseCount = atoi(token); + + // 课程和成绩 + for (int i = 0; i < student->courseCount && i < MAX_COURSES; i++) { + // 课程名称 + token = strtok(NULL, ","); + if (token == NULL) break; + strncpy(student->courses[i], token, MAX_COURSE_NAME_LENGTH - 1); + + // 成绩 + token = strtok(NULL, ","); + if (token == NULL) break; + student->scores[i] = atof(token); + } + + // 总分 + token = strtok(NULL, ","); + if (token != NULL) { + student->totalScore = atof(token); + } + + // 平均分 + token = strtok(NULL, ","); + if (token != NULL) { + student->averageScore = atof(token); + } + + studentCount++; + } + + fclose(file); + + // 更新统计信息 + statsNeedUpdate = true; +} + +/** + * @brief 将学生数据保存到CSV文件 + */ +void saveStudentsToFile() { + FILE* file = fopen(STUDENTS_FILE, "w"); + if (file == NULL) { + printError("无法保存学生数据!"); + return; + } + + // 写入CSV头部 + fprintf(file, "学号,姓名,年龄,性别,课程数量"); + for (int i = 0; i < MAX_COURSES; i++) { + fprintf(file, ",课程%d,成绩%d", i+1, i+1); + } + fprintf(file, ",总分,平均分\n"); + + // 写入学生数据 + for (int i = 0; i < studentCount; i++) { + Student* student = &students[i]; + + // 基本信息 + fprintf(file, "%s,%s,%d,%c,%d", + student->studentID, + student->name, + student->age, + student->gender, + student->courseCount); + + // 课程和成绩 + for (int j = 0; j < MAX_COURSES; j++) { + if (j < student->courseCount) { + fprintf(file, ",%s,%.2f", student->courses[j], student->scores[j]); + } else { + fprintf(file, ",,"); // 空的课程和成绩 + } + } + + // 总分和平均分 + fprintf(file, ",%.2f,%.2f\n", student->totalScore, student->averageScore); + } + + fclose(file); + dataModified = false; + + printSuccess("学生数据已保存到CSV文件!"); +} + +/** + * @brief 添加学生信息 + */ +void addStudent() { + clearScreen(); + printHeader("添加学生信息"); + + if (studentCount >= MAX_STUDENTS) { + printError("学生数量已达上限!"); + pauseSystem(); + return; + } + + Student newStudent; + memset(&newStudent, 0, sizeof(Student)); + + printf("\n"); + + // 输入学号 + while (1) { + safeInputString("请输入学号", newStudent.studentID, MAX_ID_LENGTH); + + if (!isValidStudentID(newStudent.studentID)) { + printError("学号格式无效!"); + continue; + } + + // 检查学号是否已存在 + bool exists = false; + for (int i = 0; i < studentCount; i++) { + if (strcmp(students[i].studentID, newStudent.studentID) == 0) { + printError("学号已存在!"); + exists = true; + break; + } + } + + if (!exists) break; + } + + // 输入姓名 + while (1) { + safeInputString("请输入姓名", newStudent.name, MAX_NAME_LENGTH); + if (isValidName(newStudent.name)) break; + printError("姓名格式无效!"); + } + + // 输入年龄 + newStudent.age = safeInputInt("请输入年龄", 10, 100); + + // 输入性别 + while (1) { + printf("请输入性别 (M/F): "); + char gender; + scanf(" %c", &gender); + clearInputBuffer(); + + if (isValidGender(gender)) { + newStudent.gender = gender; + break; + } + printError("性别输入无效!请输入 M 或 F"); + } + + // 输入课程信息 + printf("\n开始输入课程信息:\n"); + newStudent.courseCount = 0; + + while (newStudent.courseCount < MAX_COURSES) { + printf("\n第 %d 门课程:\n", newStudent.courseCount + 1); + + safeInputString("课程名称", + newStudent.courses[newStudent.courseCount], + MAX_COURSE_NAME_LENGTH); + + newStudent.scores[newStudent.courseCount] = + safeInputFloat("课程分数", MIN_SCORE, MAX_SCORE); + + newStudent.courseCount++; + + if (newStudent.courseCount < MAX_COURSES) { + printf("\n是否继续添加课程?(y/n): "); + char choice; + scanf(" %c", &choice); + clearInputBuffer(); + + if (choice != 'y' && choice != 'Y') { + break; + } + } + } + + // 计算总分和平均分 + calculateStudentStats(&newStudent); + + // 添加到数组 + students[studentCount] = newStudent; + studentCount++; + + // 标记数据已修改 + dataModified = true; + statsNeedUpdate = true; + + printSuccess("学生信息添加成功!"); + printf("学号: %s\n", newStudent.studentID); + printf("姓名: %s\n", newStudent.name); + printf("总分: %.2f\n", newStudent.totalScore); + printf("平均分: %.2f\n", newStudent.averageScore); + + pauseSystem(); +} + +/** + * @brief 删除学生信息 + */ +void deleteStudent() { + clearScreen(); + printHeader("删除学生信息"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + char studentID[MAX_ID_LENGTH]; + printf("\n"); + safeInputString("请输入要删除的学生学号", studentID, MAX_ID_LENGTH); + + // 查找学生 + for (int i = 0; i < studentCount; i++) { + if (strcmp(students[i].studentID, studentID) == 0) { + printf("\n找到学生信息:\n"); + printf("学号: %s\n", students[i].studentID); + printf("姓名: %s\n", students[i].name); + + printf("\n确认删除?(y/n): "); + char choice; + scanf(" %c", &choice); + clearInputBuffer(); + + if (choice == 'y' || choice == 'Y') { + // 移动后面的学生向前 + for (int j = i; j < studentCount - 1; j++) { + students[j] = students[j + 1]; + } + studentCount--; + + dataModified = true; + statsNeedUpdate = true; + + printSuccess("学生信息删除成功!"); + } else { + printInfo("删除操作已取消。"); + } + + pauseSystem(); + return; + } + } + + printError("未找到该学号的学生!"); + pauseSystem(); +} + +/** + * @brief 修改学生信息 + */ +void modifyStudent() { + clearScreen(); + printHeader("修改学生信息"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + char studentID[MAX_ID_LENGTH]; + printf("\n"); + safeInputString("请输入要修改的学生学号", studentID, MAX_ID_LENGTH); + + // 查找学生 + for (int i = 0; i < studentCount; i++) { + if (strcmp(students[i].studentID, studentID) == 0) { + printf("\n找到学生信息:\n"); + printf("学号: %s\n", students[i].studentID); + printf("姓名: %s\n", students[i].name); + printf("年龄: %d\n", students[i].age); + printf("性别: %c\n", students[i].gender); + + printf("\n修改选项:\n"); + printf("1. 修改姓名\n"); + printf("2. 修改年龄\n"); + printf("3. 修改性别\n"); + printf("4. 修改课程成绩\n"); + printf("0. 返回\n"); + + int choice = safeInputInt("请选择修改项", 0, 4); + + switch (choice) { + case 1: // 修改姓名 + safeInputString("请输入新姓名", students[i].name, MAX_NAME_LENGTH); + break; + case 2: + students[i].age = safeInputInt("请输入新年龄", 10, 100); + break; + case 3: + while (1) { + printf("请输入新性别 (M/F): "); + char gender; + scanf(" %c", &gender); + clearInputBuffer(); + + if (isValidGender(gender)) { + students[i].gender = gender; + break; + } + printError("性别输入无效!"); + } + break; + case 4: + printf("\n当前课程列表:\n"); + for (int j = 0; j < students[i].courseCount; j++) { + printf("%d. %s: %.2f\n", j + 1, students[i].courses[j], students[i].scores[j]); + } + + printf("\n修改选项:\n"); + printf("1. 修改现有课程成绩\n"); + printf("2. 添加新课程\n"); + printf("3. 删除课程\n"); + printf("0. 返回\n"); + + int courseChoice = safeInputInt("请选择操作", 0, 3); + switch (courseChoice) { + case 1: // 修改现有课程成绩 + if (students[i].courseCount == 0) { + printWarning("该学生没有课程记录!"); + break; + } + + int courseIndex = safeInputInt("请选择要修改的课程", 1, students[i].courseCount) - 1; + students[i].scores[courseIndex] = safeInputFloat("新成绩", MIN_SCORE, MAX_SCORE); + break; + + case 2: // 添加新课程 + if (students[i].courseCount >= MAX_COURSES) { + printWarning("课程数量已达上限!"); + break; + } + + safeInputString("课程名称", students[i].courses[students[i].courseCount], MAX_COURSE_NAME_LENGTH); + students[i].scores[students[i].courseCount] = safeInputFloat("课程成绩", MIN_SCORE, MAX_SCORE); + students[i].courseCount++; + break; + + case 3: // 删除课程 + if (students[i].courseCount == 0) { + printWarning("该学生没有课程记录!"); + break; + } + + courseIndex = safeInputInt("请选择要删除的课程", 1, students[i].courseCount) - 1; + + // 移动数组元素 + for (int k = courseIndex; k < students[i].courseCount - 1; k++) { + strcpy(students[i].courses[k], students[i].courses[k + 1]); + students[i].scores[k] = students[i].scores[k + 1]; + } + students[i].courseCount--; + break; + } + break; + case 0: + return; + } + + if (choice == 4) { + calculateStudentStats(&students[i]); + } + + dataModified = true; + statsNeedUpdate = true; + + printSuccess("学生信息修改成功!"); + pauseSystem(); + return; + } + } + + printError("未找到该学号的学生!"); + pauseSystem(); +} + +/** + * @brief 修改学生课程成绩 + */ +/** + * @brief 按学号查找学生 + */ +void searchStudentByID() { + clearScreen(); + printHeader("按学号查找学生"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + char studentID[MAX_ID_LENGTH]; + printf("\n"); + safeInputString("请输入学号", studentID, MAX_ID_LENGTH); + + for (int i = 0; i < studentCount; i++) { + if (strcmp(students[i].studentID, studentID) == 0) { + displayStudentInfo(&students[i]); + pauseSystem(); + return; + } + } + + printError("未找到该学号的学生!"); + pauseSystem(); +} + +/** + * @brief 按姓名查找学生 + */ +void searchStudentByName() { + clearScreen(); + printHeader("按姓名查找学生"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + char name[MAX_NAME_LENGTH]; + printf("\n"); + safeInputString("请输入姓名(支持模糊查找)", name, MAX_NAME_LENGTH); + + bool found = false; + for (int i = 0; i < studentCount; i++) { + if (strstr(students[i].name, name) != NULL) { + if (!found) { + printf("\n找到以下匹配的学生:\n"); + printSeparator(); + found = true; + } + displayStudentInfo(&students[i]); + printf("\n"); + } + } + + if (!found) { + printError("未找到匹配的学生!"); + } + + pauseSystem(); +} + +/** + * @brief 显示所有学生信息 + */ +void displayAllStudents() { + clearScreen(); + printHeader("所有学生信息"); + + if (studentCount == 0) { + printWarning("暂无学生数据!"); + pauseSystem(); + return; + } + + printf("\n"); + // 调整中文表头的对齐格式,考虑中文字符的显示宽度 + printf("%-10s %-12s %-6s %-6s %-8s %-8s\n", + "学号", "姓名", "年龄", "性别", "总分", "平均分"); + printf("========================================\n"); + + for (int i = 0; i < studentCount; i++) { + printf("%-10s %-12s %-6d %-6c %-8.2f %-8.2f\n", + students[i].studentID, + students[i].name, + students[i].age, + students[i].gender, + students[i].totalScore, + students[i].averageScore); + } + + printf("\n总学生数: %d\n", studentCount); + pauseSystem(); +} + +/** + * @brief 显示单个学生详细信息 + */ +void displayStudentInfo(const Student* student) { + printf("\n学生详细信息:\n"); + printSeparator(); + printf("学号: %s\n", student->studentID); + printf("姓名: %s\n", student->name); + printf("年龄: %d\n", student->age); + printf("性别: %c\n", student->gender); + printf("课程数量: %d\n", student->courseCount); + + if (student->courseCount > 0) { + printf("\n课程成绩:\n"); + for (int i = 0; i < student->courseCount; i++) { + printf(" %s: %.2f分\n", + student->courses[i], + student->scores[i]); + } + printf("\n总分: %.2f\n", student->totalScore); + printf("平均分: %.2f\n", student->averageScore); + } +} + +/** + * @brief 排序学生信息 + */ +void sortStudents(int criteria, int order) { + if (studentCount <= 1) return; + + // 使用冒泡排序 + for (int i = 0; i < studentCount - 1; i++) { + for (int j = 0; j < studentCount - 1 - i; j++) { + bool shouldSwap = false; + + switch (criteria) { + case SORT_BY_ID: + shouldSwap = (order == SORT_ASCENDING) ? + strcmp(students[j].studentID, students[j + 1].studentID) > 0 : + strcmp(students[j].studentID, students[j + 1].studentID) < 0; + break; + case SORT_BY_NAME: + shouldSwap = (order == SORT_ASCENDING) ? + strcmp(students[j].name, students[j + 1].name) > 0 : + strcmp(students[j].name, students[j + 1].name) < 0; + break; + case SORT_BY_TOTAL_SCORE: + shouldSwap = (order == SORT_ASCENDING) ? + students[j].totalScore > students[j + 1].totalScore : + students[j].totalScore < students[j + 1].totalScore; + break; + case SORT_BY_AVERAGE_SCORE: + shouldSwap = (order == SORT_ASCENDING) ? + students[j].averageScore > students[j + 1].averageScore : + students[j].averageScore < students[j + 1].averageScore; + break; + } + + if (shouldSwap) { + Student temp = students[j]; + students[j] = students[j + 1]; + students[j + 1] = temp; + } + } + } + + dataModified = true; +} \ No newline at end of file diff --git a/student_system.exe b/student_system.exe new file mode 100644 index 0000000000000000000000000000000000000000..b6c39753e2bcebd6717e72655055c5a6e0ce0364 GIT binary patch literal 100360 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 HcKG@KG-_Y` literal 0 HcmV?d00001 diff --git a/student_system_new.exe b/student_system_new.exe new file mode 100644 index 0000000000000000000000000000000000000000..011f1f382b7ec622f81a8d4458acdd032d04da2b GIT binary patch literal 100872 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 +#include +#include +#include "user_manage.h" +#include "config.h" +#include "globals.h" +#include "auxiliary.h" + +/** + * @brief 处理用户登录 + * @return 登录成功返回1,失败返回0 + */ +int loginSystem() { + char username[MAX_USERNAME_LENGTH]; + char password[MAX_PASSWORD_LENGTH]; + + printf("\n"); + safeInputString("请输入用户名", username, MAX_USERNAME_LENGTH); + safeInputString("请输入密码", password, MAX_PASSWORD_LENGTH); + + // 验证用户名和密码 + for (int i = 0; i < userCount; i++) { + if (strcmp(users[i].username, username) == 0 && + strcmp(users[i].password, password) == 0) { + // 登录成功 + strcpy(currentUser, username); + isCurrentUserAdmin = users[i].isAdmin; + return 1; + } + } + + return 0; // 登录失败 +} + +/** + * @brief 从文件加载用户数据 + */ +void loadUsersFromFile() { + FILE* file = fopen(USERS_FILE, "r"); + if (file == NULL) { + // 文件不存在,创建默认管理员账户 + strcpy(users[0].username, "admin"); + strcpy(users[0].password, "123456"); + users[0].isAdmin = true; + + strcpy(users[1].username, "teacher"); + strcpy(users[1].password, "password"); + users[1].isAdmin = false; + + userCount = 2; + + // 保存默认用户到文件 + saveUsersToFile(); + printInfo("已创建默认用户账户:"); + printInfo("管理员 - 用户名: admin, 密码: 123456"); + printInfo("普通用户 - 用户名: teacher, 密码: password"); + return; + } + + userCount = 0; + char line[200]; + + while (fgets(line, sizeof(line), file) && userCount < MAX_USERS) { + // 移除换行符 + line[strcspn(line, "\n")] = '\0'; + + // 解析格式:username:password:isAdmin + char* username = strtok(line, ":"); + char* password = strtok(NULL, ":"); + char* adminFlag = strtok(NULL, ":"); + + if (username && password && adminFlag) { + strcpy(users[userCount].username, username); + strcpy(users[userCount].password, password); + users[userCount].isAdmin = (strcmp(adminFlag, "1") == 0); + userCount++; + } + } + + fclose(file); +} + +/** + * @brief 将用户数据保存到文件 + */ +void saveUsersToFile() { + FILE* file = fopen(USERS_FILE, "w"); + if (file == NULL) { + printError("无法保存用户数据!"); + return; + } + + for (int i = 0; i < userCount; i++) { + fprintf(file, "%s:%s:%d\n", + users[i].username, + users[i].password, + users[i].isAdmin ? 1 : 0); + } + + fclose(file); +} + +/** + * @brief 增加用户账户 + */ +void addUserAccount() { + clearScreen(); + printHeader("添加用户账户"); + + if (userCount >= MAX_USERS) { + printError("用户数量已达上限!"); + pauseSystem(); + return; + } + + char username[MAX_USERNAME_LENGTH]; + char password[MAX_PASSWORD_LENGTH]; + + printf("\n"); + safeInputString("请输入新用户名", username, MAX_USERNAME_LENGTH); + + // 检查用户名是否已存在 + for (int i = 0; i < userCount; i++) { + if (strcmp(users[i].username, username) == 0) { + printError("用户名已存在!"); + pauseSystem(); + return; + } + } + + safeInputString("请输入密码", password, MAX_PASSWORD_LENGTH); + + printf("\n用户类型:\n"); + printf("1. 普通用户\n"); + printf("2. 管理员\n"); + int userType = safeInputInt("请选择用户类型", 1, 2); + + // 添加新用户 + strcpy(users[userCount].username, username); + strcpy(users[userCount].password, password); + users[userCount].isAdmin = (userType == 2); + userCount++; + + // 保存到文件 + saveUsersToFile(); + dataModified = true; + + printSuccess("用户添加成功!"); + pauseSystem(); +} + +/** + * @brief 删除用户账户 + */ +void deleteUserAccount() { + clearScreen(); + printHeader("删除用户账户"); + + if (userCount <= 1) { + printError("至少需要保留一个用户账户!"); + pauseSystem(); + return; + } + + char username[MAX_USERNAME_LENGTH]; + printf("\n"); + safeInputString("请输入要删除的用户名", username, MAX_USERNAME_LENGTH); + + // 不能删除当前登录用户 + if (strcmp(username, currentUser) == 0) { + printError("不能删除当前登录的用户!"); + pauseSystem(); + return; + } + + // 查找并删除用户 + for (int i = 0; i < userCount; i++) { + if (strcmp(users[i].username, username) == 0) { + // 移动后面的用户向前 + for (int j = i; j < userCount - 1; j++) { + users[j] = users[j + 1]; + } + userCount--; + + // 保存到文件 + saveUsersToFile(); + dataModified = true; + + printSuccess("用户删除成功!"); + pauseSystem(); + return; + } + } + + printError("用户不存在!"); + pauseSystem(); +} + +/** + * @brief 修改用户密码 + */ +void modifyUserPassword() { + clearScreen(); + printHeader("修改用户密码"); + + char username[MAX_USERNAME_LENGTH]; + char newPassword[MAX_PASSWORD_LENGTH]; + + printf("\n"); + safeInputString("请输入用户名", username, MAX_USERNAME_LENGTH); + + // 查找用户 + for (int i = 0; i < userCount; i++) { + if (strcmp(users[i].username, username) == 0) { + safeInputString("请输入新密码", newPassword, MAX_PASSWORD_LENGTH); + + strcpy(users[i].password, newPassword); + + // 保存到文件 + saveUsersToFile(); + dataModified = true; + + printSuccess("密码修改成功!"); + pauseSystem(); + return; + } + } + + printError("用户不存在!"); + pauseSystem(); +} + +/** + * @brief 查看所有用户 + */ +void viewAllUsers() { + clearScreen(); + printHeader("所有用户列表"); + + if (userCount == 0) { + printWarning("暂无用户数据!"); + pauseSystem(); + return; + } + + printf("\n"); + printf("%-20s %-15s %-10s\n", "用户名", "用户类型", "状态"); + printSeparator(); + + for (int i = 0; i < userCount; i++) { + printf("%-20s %-15s %-10s\n", + users[i].username, + users[i].isAdmin ? "管理员" : "普通用户", + strcmp(users[i].username, currentUser) == 0 ? "当前用户" : ""); + } + + printf("\n总用户数: %d\n", userCount); + pauseSystem(); +} \ No newline at end of file diff --git a/user_manage.h b/user_manage.h new file mode 100644 index 0000000..ce4a0f8 --- /dev/null +++ b/user_manage.h @@ -0,0 +1,13 @@ +#ifndef USER_MANAGE_H +#define USER_MANAGE_H + +// 用户认证与管理相关函数 +int loginSystem(); // 处理用户登录 +void loadUsersFromFile(); // 从文件加载用户数据 +void saveUsersToFile(); // 将用户数据保存到文件 +void addUserAccount(); // 增加用户 +void deleteUserAccount(); // 删除用户 +void modifyUserPassword(); // 修改用户密码 +void viewAllUsers(); // 查看所有用户 + +#endif // USER_MANAGE_H \ No newline at end of file