Add files via upload
This commit is contained in:
@@ -96,17 +96,4 @@
|
|||||||
#define MSG_INVALID_INPUT "输入无效,请重新输入!"
|
#define MSG_INVALID_INPUT "输入无效,请重新输入!"
|
||||||
#define MSG_FILE_ERROR "文件操作错误!"
|
#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
|
#endif // CONFIG_H
|
||||||
+59
-4
@@ -10,9 +10,64 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
// 核心处理函数声明
|
// 核心处理函数声明
|
||||||
void handleBasicFunctions(); // 处理基本功能菜单
|
|
||||||
void handleStatistics(); // 处理统计功能菜单
|
/**
|
||||||
void handleAdminFunctions(); // 处理管理功能菜单
|
* @brief 处理基本功能菜单
|
||||||
void handleSortStudents(); // 处理学生排序功能
|
* @details 显示并处理学生信息管理的基本功能菜单循环
|
||||||
|
* 提供学生信息的增删改查、排序等核心功能
|
||||||
|
* 循环显示菜单直到用户选择返回主菜单
|
||||||
|
* @note 包含的功能:
|
||||||
|
* - 添加学生信息
|
||||||
|
* - 删除学生信息
|
||||||
|
* - 修改学生信息
|
||||||
|
* - 按学号查找学生
|
||||||
|
* - 按姓名查找学生
|
||||||
|
* - 显示所有学生
|
||||||
|
* - 学生信息排序
|
||||||
|
*/
|
||||||
|
void handleBasicFunctions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理统计功能菜单
|
||||||
|
* @details 显示并处理统计分析功能菜单循环
|
||||||
|
* 提供各种学生成绩的统计分析功能
|
||||||
|
* 循环显示菜单直到用户选择返回主菜单
|
||||||
|
* @note 包含的功能:
|
||||||
|
* - 课程统计分析
|
||||||
|
* - 成绩分布统计
|
||||||
|
* - 学生排名统计
|
||||||
|
* - 综合统计分析
|
||||||
|
*/
|
||||||
|
void handleStatistics();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理管理功能菜单
|
||||||
|
* @details 显示并处理系统管理功能菜单循环,仅限管理员用户访问
|
||||||
|
* 提供用户账户管理的各项功能
|
||||||
|
* 循环显示菜单直到用户选择返回主菜单
|
||||||
|
* @note 包含的功能:
|
||||||
|
* - 添加用户账户
|
||||||
|
* - 删除用户账户
|
||||||
|
* - 修改用户密码
|
||||||
|
* - 查看所有用户
|
||||||
|
* @warning 此函数仅应在验证用户为管理员后调用
|
||||||
|
*/
|
||||||
|
void handleAdminFunctions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理学生排序功能
|
||||||
|
* @details 提供交互式的学生信息排序功能
|
||||||
|
* 用户可选择排序依据(学号、姓名、总分、平均分)和排序顺序(升序、降序)
|
||||||
|
* 排序完成后自动显示排序结果
|
||||||
|
* @note 排序依据选项:
|
||||||
|
* 1. 按学号排序
|
||||||
|
* 2. 按姓名排序
|
||||||
|
* 3. 按总分排序
|
||||||
|
* 4. 按平均分排序
|
||||||
|
* @note 排序顺序选项:
|
||||||
|
* 1. 升序
|
||||||
|
* 2. 降序
|
||||||
|
*/
|
||||||
|
void handleSortStudents();
|
||||||
|
|
||||||
#endif // CORE_HANDLERS_H
|
#endif // CORE_HANDLERS_H
|
||||||
+22
-2
@@ -10,7 +10,27 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// 文件操作函数
|
// 文件操作函数
|
||||||
bool fileExists(const char* filename); // 检查文件是否存在
|
|
||||||
bool createDirectory(const char* path); // 创建目录
|
/**
|
||||||
|
* @brief 检查文件是否存在
|
||||||
|
* @details 使用access函数(Unix/Linux)或_access函数(Windows)检查文件是否存在且可读
|
||||||
|
* @param filename 要检查的文件路径
|
||||||
|
* @return 如果文件存在且可读返回true,否则返回false
|
||||||
|
* @note 函数只检查文件是否存在,不检查文件内容
|
||||||
|
* @warning 如果filename为NULL,返回false
|
||||||
|
*/
|
||||||
|
bool fileExists(const char* filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建目录
|
||||||
|
* @details 使用mkdir函数创建指定路径的目录
|
||||||
|
* 在Windows下使用_mkdir,在Unix/Linux下使用mkdir
|
||||||
|
* @param path 要创建的目录路径
|
||||||
|
* @return 如果目录创建成功或已存在返回true,否则返回false
|
||||||
|
* @note 如果目录已存在,函数返回true
|
||||||
|
* @note 函数不会递归创建父目录
|
||||||
|
* @warning 如果path为NULL,返回false
|
||||||
|
*/
|
||||||
|
bool createDirectory(const char* path);
|
||||||
|
|
||||||
#endif // FILE_UTILS_H
|
#endif // FILE_UTILS_H
|
||||||
@@ -23,3 +23,10 @@ float overallAverageScore = 0.0; // 全体学生平均分
|
|||||||
float highestScore = 0.0; // 最高分
|
float highestScore = 0.0; // 最高分
|
||||||
float lowestScore = 100.0; // 最低分
|
float lowestScore = 100.0; // 最低分
|
||||||
bool statsNeedUpdate = true; // 统计信息是否需要更新
|
bool statsNeedUpdate = true; // 统计信息是否需要更新
|
||||||
|
|
||||||
|
// 排序参数
|
||||||
|
int currentSortCriteria = 0; // 当前排序依据
|
||||||
|
int currentSortOrder = 0; // 当前排序顺序
|
||||||
|
|
||||||
|
// 统计缓存
|
||||||
|
StatisticsCache statsCache = {false, {0}, {0}, {{0}}, 0, 0}; // 统计分析缓存
|
||||||
@@ -8,14 +8,7 @@
|
|||||||
#define GLOBALS_H
|
#define GLOBALS_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "config.h"
|
#include "types.h"
|
||||||
|
|
||||||
// 用户结构体定义
|
|
||||||
typedef struct {
|
|
||||||
char username[MAX_USERNAME_LENGTH];
|
|
||||||
char password[MAX_PASSWORD_LENGTH];
|
|
||||||
bool isAdmin; // 是否为管理员
|
|
||||||
} User;
|
|
||||||
|
|
||||||
// 全局变量声明
|
// 全局变量声明
|
||||||
extern Student students[MAX_STUDENTS]; // 学生数组
|
extern Student students[MAX_STUDENTS]; // 学生数组
|
||||||
@@ -35,4 +28,11 @@ extern float highestScore; // 最高分
|
|||||||
extern float lowestScore; // 最低分
|
extern float lowestScore; // 最低分
|
||||||
extern bool statsNeedUpdate; // 统计信息是否需要更新
|
extern bool statsNeedUpdate; // 统计信息是否需要更新
|
||||||
|
|
||||||
|
// 排序参数
|
||||||
|
extern int currentSortCriteria; // 当前排序依据
|
||||||
|
extern int currentSortOrder; // 当前排序顺序
|
||||||
|
|
||||||
|
// 统计缓存
|
||||||
|
extern StatisticsCache statsCache; // 统计分析缓存
|
||||||
|
|
||||||
#endif // GLOBALS_H
|
#endif // GLOBALS_H
|
||||||
+126
-13
@@ -10,22 +10,135 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// 界面显示函数
|
// 界面显示函数
|
||||||
void clearInputBuffer(); // 清理输入缓冲区
|
|
||||||
void pauseSystem(); // 暂停系统,等待用户按键
|
/**
|
||||||
void clearScreen(); // 清屏
|
* @brief 清理输入缓冲区
|
||||||
void printSeparator(); // 打印分隔线
|
* @details 清除标准输入流中的所有剩余字符,直到遇到换行符或文件结束符
|
||||||
void printHeader(const char* title); // 打印标题头
|
* 主要用于防止输入缓冲区中的残留字符影响后续输入操作
|
||||||
|
* @note 在使用scanf等函数后调用此函数可以避免输入问题
|
||||||
|
*/
|
||||||
|
void clearInputBuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 暂停系统,等待用户按键
|
||||||
|
* @details 显示提示信息并等待用户按下任意键后继续执行
|
||||||
|
* 在Windows系统下使用_getch()函数,在其他系统下使用getchar()函数
|
||||||
|
* @note 用于在菜单操作完成后暂停,让用户有时间查看结果
|
||||||
|
* @warning 在非Windows系统下需要按回车键才能继续
|
||||||
|
*/
|
||||||
|
void pauseSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清屏
|
||||||
|
* @details 根据操作系统类型调用相应的清屏命令
|
||||||
|
* Windows系统使用"cls"命令,其他系统使用"clear"命令
|
||||||
|
* @note 用于清除终端屏幕内容,提供更好的用户界面体验
|
||||||
|
* @warning 依赖于系统命令,在某些受限环境下可能无法正常工作
|
||||||
|
*/
|
||||||
|
void clearScreen();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 打印分隔线
|
||||||
|
* @details 输出一行由等号组成的分隔线,用于美化界面显示
|
||||||
|
* @note 分隔线长度为40个字符,用于分隔不同的界面区域
|
||||||
|
*/
|
||||||
|
void printSeparator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 打印标题头
|
||||||
|
* @details 以美观的格式显示标题,标题上下各有一条分隔线
|
||||||
|
* @param title 要显示的标题文本,不能为NULL
|
||||||
|
* @note 标题会居中显示,前面有10个空格的缩进
|
||||||
|
* @warning 如果title为NULL,可能导致程序崩溃
|
||||||
|
*/
|
||||||
|
void printHeader(const char* title);
|
||||||
|
|
||||||
// 安全输入函数
|
// 安全输入函数
|
||||||
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); // 安全输入字符串
|
* @brief 安全输入整数
|
||||||
|
* @details 提供安全的整数输入功能,包含范围验证和错误处理
|
||||||
|
* 使用fgets和sscanf组合避免缓冲区溢出,循环直到获得有效输入
|
||||||
|
* @param prompt 显示给用户的提示信息
|
||||||
|
* @param min 允许输入的最小值(包含)
|
||||||
|
* @param max 允许输入的最大值(包含)
|
||||||
|
* @return 返回用户输入的有效整数
|
||||||
|
* @note 函数会一直循环直到用户输入有效的整数
|
||||||
|
* @note 自动显示输入范围提示
|
||||||
|
* @warning 如果prompt为NULL,printf可能出现问题
|
||||||
|
*/
|
||||||
|
int safeInputInt(const char* prompt, int min, int max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 安全输入浮点数
|
||||||
|
* @details 提供安全的浮点数输入功能,包含范围验证和错误处理
|
||||||
|
* 使用fgets和sscanf组合避免缓冲区溢出,循环直到获得有效输入
|
||||||
|
* @param prompt 显示给用户的提示信息
|
||||||
|
* @param min 允许输入的最小值(包含)
|
||||||
|
* @param max 允许输入的最大值(包含)
|
||||||
|
* @return 返回用户输入的有效浮点数
|
||||||
|
* @note 函数会一直循环直到用户输入有效的浮点数
|
||||||
|
* @note 自动显示输入范围提示,精度为小数点后1位
|
||||||
|
* @warning 如果prompt为NULL,printf可能出现问题
|
||||||
|
*/
|
||||||
|
float safeInputFloat(const char* prompt, float min, float max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 安全输入字符串
|
||||||
|
* @details 提供安全的字符串输入功能,包含空值检查和自动去除首尾空格
|
||||||
|
* 使用fgets避免缓冲区溢出,自动移除换行符并处理空白字符
|
||||||
|
* @param prompt 显示给用户的提示信息
|
||||||
|
* @param buffer 存储输入字符串的缓冲区
|
||||||
|
* @param maxLen 缓冲区的最大长度(包含终止符)
|
||||||
|
* @note 函数会一直循环直到用户输入非空字符串
|
||||||
|
* @note 自动移除输入字符串的首尾空白字符
|
||||||
|
* @warning 如果buffer为NULL或maxLen<=0,可能导致程序崩溃
|
||||||
|
*/
|
||||||
|
void safeInputString(const char* prompt, char* buffer, int maxLen);
|
||||||
|
|
||||||
// 颜色输出函数
|
// 颜色输出函数
|
||||||
void printColored(const char* text, const char* color); // 彩色输出
|
|
||||||
void printSuccess(const char* message); // 成功消息
|
/**
|
||||||
void printError(const char* message); // 错误消息
|
* @brief 彩色输出
|
||||||
void printWarning(const char* message); // 警告消息
|
* @details 使用ANSI转义序列在终端中输出彩色文本
|
||||||
void printInfo(const char* message); // 信息消息
|
* 输出格式为:颜色代码 + 文本 + 重置代码
|
||||||
|
* @param text 要输出的文本内容
|
||||||
|
* @param color ANSI颜色代码字符串(如COLOR_RED、COLOR_GREEN等)
|
||||||
|
* @note 输出后会自动重置颜色为默认值
|
||||||
|
* @warning 如果终端不支持ANSI转义序列,可能显示乱码
|
||||||
|
*/
|
||||||
|
void printColored(const char* text, const char* color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 成功消息
|
||||||
|
* @details 以绿色显示成功消息,用于提示操作成功完成
|
||||||
|
* @param message 要显示的成功消息文本
|
||||||
|
* @note 消息会以绿色显示,并在末尾自动添加换行符
|
||||||
|
*/
|
||||||
|
void printSuccess(const char* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 错误消息
|
||||||
|
* @details 以红色显示错误消息,用于提示操作失败或出现错误
|
||||||
|
* @param message 要显示的错误消息文本
|
||||||
|
* @note 消息会以红色显示,并在末尾自动添加换行符
|
||||||
|
*/
|
||||||
|
void printError(const char* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 警告消息
|
||||||
|
* @details 以黄色显示警告消息,用于提示需要注意的情况
|
||||||
|
* @param message 要显示的警告消息文本
|
||||||
|
* @note 消息会以黄色显示,并在末尾自动添加换行符
|
||||||
|
*/
|
||||||
|
void printWarning(const char* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 信息消息
|
||||||
|
* @details 以青色显示信息消息,用于提示一般性信息
|
||||||
|
* @param message 要显示的信息消息文本
|
||||||
|
* @note 消息会以青色显示,并在末尾自动添加换行符
|
||||||
|
*/
|
||||||
|
void printInfo(const char* message);
|
||||||
|
|
||||||
#endif // IO_UTILS_H
|
#endif // IO_UTILS_H
|
||||||
@@ -1,8 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* @file main.c
|
* @brief 主程序入口
|
||||||
* @brief 学生成绩管理系统主程序
|
* @brief 学生成绩管理系统主程序
|
||||||
* @note 系统入口点,包含主要的程序流程控制
|
* @details 学生成绩管理系统的主入口函数,负责系统初始化、用户登录验证、
|
||||||
*/
|
* 主菜单循环处理和系统清理等核心流程
|
||||||
|
* 程序流程:设置编码 -> 系统初始化 -> 用户登录 -> 主菜单循环 -> 数据保存 -> 系统清理
|
||||||
|
* @param argc 命令行参数个数(当前未使用)
|
||||||
|
* @param argv 命令行参数数组(当前未使用)
|
||||||
|
* @return 程序退出状态码:0表示正常退出,-1表示异常退出
|
||||||
|
* @note 系统预设用户账户:
|
||||||
|
* 1. admin - 密码:123456(管理员权限)
|
||||||
|
* 2. teacher - 密码:password(普通用户权限)
|
||||||
|
* @note 编译运行命令:
|
||||||
|
* gcc -o student_system.exe *.c -I.
|
||||||
|
./student_system
|
||||||
|
* @warning 登录失败超过MAX_LOGIN_ATTEMPTS次会强制退出程序
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -25,22 +37,6 @@
|
|||||||
#include "core_handlers.h"
|
#include "core_handlers.h"
|
||||||
#include "student_io.h"
|
#include "student_io.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 主程序入口
|
|
||||||
* @details 学生成绩管理系统的主入口函数,负责系统初始化、用户登录验证、
|
|
||||||
* 主菜单循环处理和系统清理等核心流程
|
|
||||||
* 程序流程:设置编码 -> 系统初始化 -> 用户登录 -> 主菜单循环 -> 数据保存 -> 系统清理
|
|
||||||
* @param argc 命令行参数个数(当前未使用)
|
|
||||||
* @param argv 命令行参数数组(当前未使用)
|
|
||||||
* @return 程序退出状态码:0表示正常退出,-1表示异常退出
|
|
||||||
* @note 系统预设用户账户:
|
|
||||||
* 1. admin - 密码:123456(管理员权限)
|
|
||||||
* 2. teacher - 密码:password(普通用户权限)
|
|
||||||
* @note 编译运行命令:
|
|
||||||
* gcc -Wall -Wextra -std=c99 -g main.c globals.c main_menu.c user_manage.c core_handlers.c statistical_analysis.c student_io.c student_crud.c student_search.c student_sort.c io_utils.c validation.c string_utils.c file_utils.c math_utils.c system_utils.c -o student_system
|
|
||||||
./student_system
|
|
||||||
* @warning 登录失败超过MAX_LOGIN_ATTEMPTS次会强制退出程序
|
|
||||||
*/
|
|
||||||
int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
|
int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
|
||||||
{
|
{
|
||||||
// 设置控制台编码为UTF-8
|
// 设置控制台编码为UTF-8
|
||||||
|
|||||||
+61
-4
@@ -1,12 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* @file main_menu.h
|
||||||
|
* @brief 主菜单实现文件
|
||||||
|
* @note 实现各种菜单显示功能函数声明
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MAIN_MENU_H
|
#ifndef MAIN_MENU_H
|
||||||
#define MAIN_MENU_H
|
#define MAIN_MENU_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
// 主菜单和子菜单显示函数
|
// 主菜单和子菜单显示函数
|
||||||
void displayMainMenu(); // 显示主菜单
|
|
||||||
void displayBasicFunctionsMenu(); // 显示基本功能菜单
|
/**
|
||||||
void displayStatisticsMenu(); // 显示统计功能菜单
|
* @brief 显示主菜单
|
||||||
void displayAdminMenu(); // 显示管理功能菜单
|
* @details 显示学生成绩管理系统的主菜单界面,包括当前用户信息和可用功能选项
|
||||||
|
* 根据用户权限动态显示菜单项(管理员可看到系统管理功能)
|
||||||
|
* @note 菜单选项:
|
||||||
|
* 1. 基本功能管理(所有用户)
|
||||||
|
* 2. 统计分析功能(所有用户)
|
||||||
|
* 3. 系统管理功能(仅管理员)
|
||||||
|
* 0. 退出系统
|
||||||
|
*/
|
||||||
|
void displayMainMenu();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示基本功能菜单
|
||||||
|
* @details 显示学生信息管理的基本功能菜单,包括增删改查和排序功能
|
||||||
|
* 同时显示当前系统中的学生总数
|
||||||
|
* @note 菜单功能:
|
||||||
|
* 1. 添加学生信息
|
||||||
|
* 2. 删除学生信息
|
||||||
|
* 3. 修改学生信息
|
||||||
|
* 4. 按学号查找学生
|
||||||
|
* 5. 按姓名查找学生
|
||||||
|
* 6. 显示所有学生
|
||||||
|
* 7. 学生信息排序
|
||||||
|
* 0. 返回主菜单
|
||||||
|
*/
|
||||||
|
void displayBasicFunctionsMenu();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示统计功能菜单
|
||||||
|
* @details 显示统计分析功能菜单,提供各种数据统计和分析选项
|
||||||
|
* 显示当前学生总数和系统平均分(如果有学生数据)
|
||||||
|
* @note 菜单功能:
|
||||||
|
* 1. 课程统计分析
|
||||||
|
* 2. 成绩分布统计
|
||||||
|
* 3. 分数段统计
|
||||||
|
* 4. 综合统计分析
|
||||||
|
* 0. 返回主菜单
|
||||||
|
*/
|
||||||
|
void displayStatisticsMenu();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示管理功能菜单
|
||||||
|
* @details 显示系统管理功能菜单,仅管理员可访问
|
||||||
|
* 提供用户账户管理功能,显示当前用户总数
|
||||||
|
* @note 菜单功能:
|
||||||
|
* 1. 添加用户账户
|
||||||
|
* 2. 删除用户账户
|
||||||
|
* 3. 修改用户密码
|
||||||
|
* 4. 查看所有用户
|
||||||
|
* 0. 返回主菜单
|
||||||
|
* @warning 此菜单仅限管理员用户访问
|
||||||
|
*/
|
||||||
|
void displayAdminMenu();
|
||||||
|
|
||||||
#endif // MAIN_MENU_H
|
#endif // MAIN_MENU_H
|
||||||
+11
-2
@@ -7,7 +7,16 @@
|
|||||||
#ifndef MATH_UTILS_H
|
#ifndef MATH_UTILS_H
|
||||||
#define MATH_UTILS_H
|
#define MATH_UTILS_H
|
||||||
|
|
||||||
// 数学计算函数
|
/**
|
||||||
float calculateAverage(float scores[], int count); // 计算平均值
|
* @brief 计算平均值
|
||||||
|
* @details 计算浮点数数组的算术平均值
|
||||||
|
* 遍历数组求和,然后除以元素个数
|
||||||
|
* @param scores 浮点数数组
|
||||||
|
* @param count 数组元素个数
|
||||||
|
* @return 返回数组的平均值,如果count为0返回0.0
|
||||||
|
* @note 如果count为0,函数返回0.0避免除零错误
|
||||||
|
* @warning 如果scores为NULL且count>0,可能导致程序崩溃
|
||||||
|
*/
|
||||||
|
float calculateAverage(float scores[], int count);
|
||||||
|
|
||||||
#endif // MATH_UTILS_H
|
#endif // MATH_UTILS_H
|
||||||
+211
-24
@@ -8,6 +8,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include "statistical_analysis.h"
|
#include "statistical_analysis.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
@@ -173,7 +174,8 @@ void displayScoreDistribution()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScoreDistribution dist = calculateScoreDistribution();
|
// 使用缓存的分数分布数据
|
||||||
|
ScoreDistribution dist = getCachedScoreDistribution();
|
||||||
|
|
||||||
printf("\n分数段分布:\n");
|
printf("\n分数段分布:\n");
|
||||||
printSeparator();
|
printSeparator();
|
||||||
@@ -263,28 +265,9 @@ void displayStudentRanking()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建排名数组
|
// 使用缓存的排名数据
|
||||||
StudentRank rankings[MAX_STUDENTS];
|
StudentRank rankings[MAX_STUDENTS];
|
||||||
for (int i = 0; i < studentCount; i++)
|
int rankingCount = getCachedStudentRankings(rankings);
|
||||||
{
|
|
||||||
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("\n");
|
||||||
// 调整中文表头的对齐格式,考虑中文字符的显示宽度
|
// 调整中文表头的对齐格式,考虑中文字符的显示宽度
|
||||||
@@ -292,7 +275,7 @@ void displayStudentRanking()
|
|||||||
"排名", "学号", "姓名", "总分", "平均分");
|
"排名", "学号", "姓名", "总分", "平均分");
|
||||||
printf("==========================================\n");
|
printf("==========================================\n");
|
||||||
|
|
||||||
for (int i = 0; i < studentCount; i++)
|
for (int i = 0; i < rankingCount; i++)
|
||||||
{
|
{
|
||||||
int idx = rankings[i].studentIndex;
|
int idx = rankings[i].studentIndex;
|
||||||
printf("%-5d %-10s %-12s %-8.2f %-8.2f\n",
|
printf("%-5d %-10s %-12s %-8.2f %-8.2f\n",
|
||||||
@@ -328,7 +311,8 @@ void displayOverallStatistics()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OverallStats stats = calculateOverallStats();
|
// 使用缓存的总体统计数据
|
||||||
|
OverallStats stats = getCachedOverallStats();
|
||||||
|
|
||||||
printf("\n学生信息统计:\n");
|
printf("\n学生信息统计:\n");
|
||||||
printSeparator();
|
printSeparator();
|
||||||
@@ -651,4 +635,207 @@ void updateGlobalStats()
|
|||||||
|
|
||||||
overallAverageScore = total / studentCount;
|
overallAverageScore = total / studentCount;
|
||||||
statsNeedUpdate = false;
|
statsNeedUpdate = false;
|
||||||
|
|
||||||
|
// 使统计缓存无效
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 缓存管理函数实现 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化统计缓存
|
||||||
|
* @details 初始化统计缓存系统,清空所有缓存数据
|
||||||
|
* @note 在系统启动时调用,确保缓存处于干净状态
|
||||||
|
*/
|
||||||
|
// 快速排序辅助函数:分区
|
||||||
|
int partitionRankings(StudentRank arr[], int low, int high) {
|
||||||
|
float pivot = arr[high].averageScore;
|
||||||
|
int i = (low - 1);
|
||||||
|
|
||||||
|
for (int j = low; j <= high - 1; j++) {
|
||||||
|
// 降序排列:如果当前元素大于等于基准值
|
||||||
|
if (arr[j].averageScore >= pivot) {
|
||||||
|
i++;
|
||||||
|
StudentRank temp = arr[i];
|
||||||
|
arr[i] = arr[j];
|
||||||
|
arr[j] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StudentRank temp = arr[i + 1];
|
||||||
|
arr[i + 1] = arr[high];
|
||||||
|
arr[high] = temp;
|
||||||
|
return (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快速排序主函数
|
||||||
|
void quickSortRankings(StudentRank arr[], int low, int high) {
|
||||||
|
if (low < high) {
|
||||||
|
int pi = partitionRankings(arr, low, high);
|
||||||
|
quickSortRankings(arr, low, pi - 1);
|
||||||
|
quickSortRankings(arr, pi + 1, high);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initStatisticsCache()
|
||||||
|
{
|
||||||
|
statsCache.isValid = false;
|
||||||
|
statsCache.lastStudentCount = 0;
|
||||||
|
statsCache.lastDataHash = 0;
|
||||||
|
memset(&statsCache.overallStats, 0, sizeof(OverallStats));
|
||||||
|
memset(&statsCache.scoreDistribution, 0, sizeof(ScoreDistribution));
|
||||||
|
memset(statsCache.rankings, 0, sizeof(statsCache.rankings));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查缓存是否有效
|
||||||
|
* @details 检查统计缓存是否仍然有效,通过比较学生数量和数据哈希值
|
||||||
|
* @return bool 如果缓存有效返回true,否则返回false
|
||||||
|
* @note 当学生数据发生变化时,缓存会被标记为无效
|
||||||
|
*/
|
||||||
|
bool isCacheValid()
|
||||||
|
{
|
||||||
|
if (!statsCache.isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查学生数量是否变化
|
||||||
|
if (statsCache.lastStudentCount != studentCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查数据哈希值是否变化
|
||||||
|
unsigned long currentHash = calculateDataHash();
|
||||||
|
if (statsCache.lastDataHash != currentHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 更新统计缓存
|
||||||
|
* @details 重新计算并更新所有统计缓存数据
|
||||||
|
* @note 当缓存无效时调用,重新计算所有统计信息并更新缓存
|
||||||
|
* @note 包括总体统计、分数分布和学生排名的缓存更新
|
||||||
|
*/
|
||||||
|
void updateStatisticsCache()
|
||||||
|
{
|
||||||
|
if (studentCount == 0) {
|
||||||
|
initStatisticsCache();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新总体统计缓存
|
||||||
|
statsCache.overallStats = calculateOverallStats();
|
||||||
|
|
||||||
|
// 更新分数分布缓存
|
||||||
|
statsCache.scoreDistribution = calculateScoreDistribution();
|
||||||
|
|
||||||
|
// 更新学生排名缓存
|
||||||
|
for (int i = 0; i < studentCount; i++) {
|
||||||
|
statsCache.rankings[i].studentIndex = i;
|
||||||
|
statsCache.rankings[i].averageScore = students[i].averageScore;
|
||||||
|
statsCache.rankings[i].totalScore = students[i].totalScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用快速排序按平均分排序(降序)
|
||||||
|
quickSortRankings(statsCache.rankings, 0, studentCount - 1);
|
||||||
|
|
||||||
|
// 更新缓存状态
|
||||||
|
statsCache.isValid = true;
|
||||||
|
statsCache.lastStudentCount = studentCount;
|
||||||
|
statsCache.lastDataHash = calculateDataHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使缓存无效
|
||||||
|
* @details 将统计缓存标记为无效,强制下次访问时重新计算
|
||||||
|
* @note 当学生数据被修改时调用,确保统计数据的准确性
|
||||||
|
*/
|
||||||
|
void invalidateCache()
|
||||||
|
{
|
||||||
|
statsCache.isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算数据哈希值
|
||||||
|
* @details 计算当前学生数据的哈希值,用于检测数据变化
|
||||||
|
* @return unsigned long 当前数据的哈希值
|
||||||
|
* @note 基于学生数量、学号、成绩等关键数据计算哈希值
|
||||||
|
*/
|
||||||
|
unsigned long calculateDataHash()
|
||||||
|
{
|
||||||
|
unsigned long hash = 5381; // DJB2 哈希算法初始值
|
||||||
|
|
||||||
|
// 包含学生数量
|
||||||
|
hash = ((hash << 5) + hash) + studentCount;
|
||||||
|
|
||||||
|
for (int i = 0; i < studentCount; i++) {
|
||||||
|
// 包含学号
|
||||||
|
for (int j = 0; students[i].studentID[j] != '\0'; j++) {
|
||||||
|
hash = ((hash << 5) + hash) + students[i].studentID[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包含总分和平均分
|
||||||
|
hash = ((hash << 5) + hash) + (unsigned long)(students[i].totalScore * 100);
|
||||||
|
hash = ((hash << 5) + hash) + (unsigned long)(students[i].averageScore * 100);
|
||||||
|
|
||||||
|
// 包含课程数量
|
||||||
|
hash = ((hash << 5) + hash) + students[i].courseCount;
|
||||||
|
|
||||||
|
// 包含各科成绩
|
||||||
|
for (int j = 0; j < students[i].courseCount; j++) {
|
||||||
|
hash = ((hash << 5) + hash) + (unsigned long)(students[i].scores[j] * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓存的总体统计
|
||||||
|
* @details 获取缓存的总体统计数据,如果缓存无效则先更新缓存
|
||||||
|
* @return OverallStats 总体统计数据
|
||||||
|
* @note 优先使用缓存数据,提高查询效率
|
||||||
|
*/
|
||||||
|
OverallStats getCachedOverallStats()
|
||||||
|
{
|
||||||
|
if (!isCacheValid()) {
|
||||||
|
updateStatisticsCache();
|
||||||
|
}
|
||||||
|
return statsCache.overallStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓存的分数分布
|
||||||
|
* @details 获取缓存的分数分布数据,如果缓存无效则先更新缓存
|
||||||
|
* @return ScoreDistribution 分数分布数据
|
||||||
|
* @note 优先使用缓存数据,避免重复计算
|
||||||
|
*/
|
||||||
|
ScoreDistribution getCachedScoreDistribution()
|
||||||
|
{
|
||||||
|
if (!isCacheValid()) {
|
||||||
|
updateStatisticsCache();
|
||||||
|
}
|
||||||
|
return statsCache.scoreDistribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓存的学生排名
|
||||||
|
* @details 获取缓存的学生排名数据,如果缓存无效则先更新缓存
|
||||||
|
* @param rankings 输出参数,存储排名数据的数组
|
||||||
|
* @return int 返回排名数据的数量
|
||||||
|
* @note 排名按平均分降序排列,优先使用缓存数据
|
||||||
|
*/
|
||||||
|
int getCachedStudentRankings(StudentRank* rankings)
|
||||||
|
{
|
||||||
|
if (!isCacheValid()) {
|
||||||
|
updateStatisticsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rankings != NULL && studentCount > 0) {
|
||||||
|
memcpy(rankings, statsCache.rankings, studentCount * sizeof(StudentRank));
|
||||||
|
}
|
||||||
|
|
||||||
|
return studentCount;
|
||||||
}
|
}
|
||||||
+228
-48
@@ -7,64 +7,244 @@
|
|||||||
#ifndef STATISTICAL_ANALYSIS_H
|
#ifndef STATISTICAL_ANALYSIS_H
|
||||||
#define STATISTICAL_ANALYSIS_H
|
#define STATISTICAL_ANALYSIS_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "types.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(); // 显示学生排名
|
* @brief 显示课程统计信息
|
||||||
void displayOverallStatistics(); // 显示系统总体统计
|
* @details 统计并显示所有课程的详细信息,包括每门课程的人数、最高分、最低分、平均分和及格率
|
||||||
|
* 自动收集系统中所有不重复的课程名称,并为每门课程计算统计数据
|
||||||
|
* @note 显示内容包括:
|
||||||
|
* - 课程名称
|
||||||
|
* - 选课人数
|
||||||
|
* - 最高分、最低分、平均分
|
||||||
|
* - 及格率(基于PASS_SCORE阈值)
|
||||||
|
* @warning 如果没有学生数据或课程数据,将显示相应警告信息
|
||||||
|
*/
|
||||||
|
void displayCourseStatistics();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示分数分布
|
||||||
|
* @details 统计并显示学生平均分的分布情况,按分数段进行分类统计
|
||||||
|
* 显示各分数段的人数和百分比,以及总体及格情况
|
||||||
|
* @note 分数段划分:
|
||||||
|
* - 90-100分:优秀
|
||||||
|
* - 80-89分:良好
|
||||||
|
* - 70-79分:中等
|
||||||
|
* - 60-69分:及格
|
||||||
|
* - 0-59分:不及格
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
|
*/
|
||||||
|
void displayScoreDistribution();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示学生排名
|
||||||
|
* @details 按学生平均分进行降序排序,显示学生排名列表
|
||||||
|
* 包括排名、学号、姓名、总分和平均分信息
|
||||||
|
* @note 排序规则:按平均分从高到低排序
|
||||||
|
* @note 显示格式:排名 | 学号 | 姓名 | 总分 | 平均分
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
|
*/
|
||||||
|
void displayStudentRanking();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示系统总体统计
|
||||||
|
* @details 显示系统的综合统计信息,包括学生信息统计、成绩统计和课程统计
|
||||||
|
* 提供系统整体数据的全面概览
|
||||||
|
* @note 统计内容包括:
|
||||||
|
* - 学生信息:总数、性别分布、平均年龄
|
||||||
|
* - 成绩统计:最高/最低/平均分、标准差
|
||||||
|
* - 课程统计:总课程数、平均课程数
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
|
*/
|
||||||
|
void displayOverallStatistics();
|
||||||
|
|
||||||
// 查找功能
|
// 查找功能
|
||||||
void findTopStudent(); // 查找最高分学生
|
|
||||||
void findBottomStudent(); // 查找最低分学生
|
/**
|
||||||
void findTopScoreInCourse(); // 按课程查找最高分
|
* @brief 查找最高分学生
|
||||||
|
* @details 查找并显示平均分最高的学生信息
|
||||||
|
* 遍历所有学生,找出平均分最高者并显示其详细信息
|
||||||
|
* @note 比较依据:学生的平均分(averageScore)
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
|
*/
|
||||||
|
void findTopStudent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 查找最低分学生
|
||||||
|
* @details 查找并显示平均分最低的学生信息
|
||||||
|
* 遍历所有学生,找出平均分最低者并显示其详细信息
|
||||||
|
* @note 比较依据:学生的平均分(averageScore)
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
|
*/
|
||||||
|
void findBottomStudent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按课程查找最高分
|
||||||
|
* @details 在指定课程中查找并显示最高分学生的信息
|
||||||
|
* 用户输入课程名称,系统查找该课程的最高分获得者
|
||||||
|
* @note 查找过程:
|
||||||
|
* 1. 用户输入课程名称
|
||||||
|
* 2. 遍历所有学生的该课程成绩
|
||||||
|
* 3. 找出最高分及对应学生
|
||||||
|
* 4. 显示学生信息和分数
|
||||||
|
* @warning 如果课程不存在,将显示错误信息
|
||||||
|
*/
|
||||||
|
void findTopScoreInCourse();
|
||||||
|
|
||||||
// 计算函数
|
// 计算函数
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算课程统计信息
|
||||||
|
* @details 计算指定课程的详细统计数据,包括选课人数、分数统计和及格率
|
||||||
|
* @param courseName 要统计的课程名称
|
||||||
|
* @return CourseStats 包含课程统计信息的结构体
|
||||||
|
* @note 统计内容包括:
|
||||||
|
* - studentCount: 选课学生数量
|
||||||
|
* - maxScore, minScore: 最高分和最低分
|
||||||
|
* - totalScore, averageScore: 总分和平均分
|
||||||
|
* - passRate: 及格率(百分比)
|
||||||
|
* @warning 如果课程不存在,返回全零的统计结构体
|
||||||
|
*/
|
||||||
CourseStats calculateCourseStats(const char* courseName);
|
CourseStats calculateCourseStats(const char* courseName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算分数分布
|
||||||
|
* @details 根据学生的平均分计算各分数段的人数分布
|
||||||
|
* @return ScoreDistribution 包含各分数段人数的结构体
|
||||||
|
* @note 分数段定义:
|
||||||
|
* - excellent: 90-100分
|
||||||
|
* - good: 80-89分
|
||||||
|
* - medium: 70-79分
|
||||||
|
* - pass: 60-69分
|
||||||
|
* - fail: 0-59分
|
||||||
|
*/
|
||||||
ScoreDistribution calculateScoreDistribution();
|
ScoreDistribution calculateScoreDistribution();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算系统总体统计
|
||||||
|
* @details 计算系统的综合统计数据,包括学生、成绩和课程的各项统计指标
|
||||||
|
* @return OverallStats 包含系统总体统计信息的结构体
|
||||||
|
* @note 计算内容包括:
|
||||||
|
* - 学生统计:总数、性别分布、平均年龄
|
||||||
|
* - 成绩统计:最高/最低/平均分、标准差
|
||||||
|
* - 课程统计:总课程数、人均课程数
|
||||||
|
* @note 标准差计算使用总体标准差公式
|
||||||
|
*/
|
||||||
OverallStats calculateOverallStats();
|
OverallStats calculateOverallStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算学生统计信息
|
||||||
|
* @details 计算指定学生的总分和平均分
|
||||||
|
* 根据学生的所有课程成绩计算统计数据
|
||||||
|
* @param student 指向要计算统计信息的学生结构体的指针
|
||||||
|
* @note 计算内容:
|
||||||
|
* - totalScore: 所有课程成绩的总和
|
||||||
|
* - averageScore: 平均成绩(总分/课程数)
|
||||||
|
* @note 如果学生没有课程,总分和平均分都设为0
|
||||||
|
* @warning 传入的student指针不能为NULL
|
||||||
|
*/
|
||||||
void calculateStudentStats(Student* student);
|
void calculateStudentStats(Student* student);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 更新全局统计缓存
|
||||||
|
* @details 更新系统的全局统计缓存变量,包括全体平均分、最高分和最低分
|
||||||
|
* 当学生数据发生变化时调用此函数更新缓存
|
||||||
|
* @note 更新的全局变量:
|
||||||
|
* - overallAverageScore: 全体学生平均分
|
||||||
|
* - highestScore: 最高平均分
|
||||||
|
* - lowestScore: 最低平均分
|
||||||
|
* - statsNeedUpdate: 统计更新标志(设为false)
|
||||||
|
* @note 如果没有学生数据,所有统计值都设为0
|
||||||
|
* @see overallAverageScore, highestScore, lowestScore, statsNeedUpdate
|
||||||
|
*/
|
||||||
void updateGlobalStats();
|
void updateGlobalStats();
|
||||||
|
|
||||||
|
// 缓存管理函数
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化统计缓存
|
||||||
|
* @details 初始化统计缓存系统,清空所有缓存数据
|
||||||
|
* @note 在系统启动时调用,确保缓存处于干净状态
|
||||||
|
*/
|
||||||
|
void initStatisticsCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查缓存是否有效
|
||||||
|
* @details 检查统计缓存是否仍然有效,通过比较学生数量和数据哈希值
|
||||||
|
* @return bool 如果缓存有效返回true,否则返回false
|
||||||
|
* @note 当学生数据发生变化时,缓存会被标记为无效
|
||||||
|
*/
|
||||||
|
bool isCacheValid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 更新统计缓存
|
||||||
|
* @details 重新计算并更新所有统计缓存数据
|
||||||
|
* @note 当缓存无效时调用,重新计算所有统计信息并更新缓存
|
||||||
|
* @note 包括总体统计、分数分布和学生排名的缓存更新
|
||||||
|
*/
|
||||||
|
void updateStatisticsCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使缓存无效
|
||||||
|
* @details 将统计缓存标记为无效,强制下次访问时重新计算
|
||||||
|
* @note 当学生数据被修改时调用,确保统计数据的准确性
|
||||||
|
*/
|
||||||
|
void invalidateCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算数据哈希值
|
||||||
|
* @details 计算当前学生数据的哈希值,用于检测数据变化
|
||||||
|
* @return unsigned long 当前数据的哈希值
|
||||||
|
* @note 基于学生数量、学号、成绩等关键数据计算哈希值
|
||||||
|
*/
|
||||||
|
unsigned long calculateDataHash();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓存的总体统计
|
||||||
|
* @details 获取缓存的总体统计数据,如果缓存无效则先更新缓存
|
||||||
|
* @return OverallStats 总体统计数据
|
||||||
|
* @note 优先使用缓存数据,提高查询效率
|
||||||
|
*/
|
||||||
|
OverallStats getCachedOverallStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓存的分数分布
|
||||||
|
* @details 获取缓存的分数分布数据,如果缓存无效则先更新缓存
|
||||||
|
* @return ScoreDistribution 分数分布数据
|
||||||
|
* @note 优先使用缓存数据,避免重复计算
|
||||||
|
*/
|
||||||
|
ScoreDistribution getCachedScoreDistribution();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓存的学生排名
|
||||||
|
* @details 获取缓存的学生排名数据,如果缓存无效则先更新缓存
|
||||||
|
* @param rankings 输出参数,存储排名数据的数组
|
||||||
|
* @return int 返回排名数据的数量
|
||||||
|
* @note 排名按平均分降序排列,优先使用缓存数据
|
||||||
|
*/
|
||||||
|
int getCachedStudentRankings(StudentRank* rankings);
|
||||||
|
|
||||||
|
// 排序优化函数
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 快速排序分区函数
|
||||||
|
* @details 对学生排名数组进行分区操作,用于快速排序
|
||||||
|
* @param arr 要分区的学生排名数组
|
||||||
|
* @param low 分区的起始索引
|
||||||
|
* @param high 分区的结束索引
|
||||||
|
* @return int 分区点的索引
|
||||||
|
*/
|
||||||
|
int partitionRankings(StudentRank arr[], int low, int high);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 快速排序函数
|
||||||
|
* @details 对学生排名数组按平均分进行快速排序(降序)
|
||||||
|
* @param arr 要排序的学生排名数组
|
||||||
|
* @param low 排序的起始索引
|
||||||
|
* @param high 排序的结束索引
|
||||||
|
*/
|
||||||
|
void quickSortRankings(StudentRank arr[], int low, int high);
|
||||||
|
|
||||||
#endif // STATISTICAL_ANALYSIS_H
|
#endif // STATISTICAL_ANALYSIS_H
|
||||||
+21
-3
@@ -9,8 +9,26 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// 字符串处理函数
|
/**
|
||||||
void trimString(char* str); // 去除字符串首尾空白字符
|
* @brief 去除字符串首尾空白字符
|
||||||
bool isEmptyString(const char* str); // 检查字符串是否为空
|
* @details 移除字符串开头和结尾的空格、制表符、换行符等空白字符
|
||||||
|
* 使用双指针技术,从两端向中间处理,原地修改字符串
|
||||||
|
* @param str 要处理的字符串,函数会直接修改此字符串
|
||||||
|
* @note 函数会直接修改传入的字符串,不会分配新内存
|
||||||
|
* @note 如果整个字符串都是空白字符,结果将是空字符串
|
||||||
|
* @warning 如果str为NULL,可能导致程序崩溃
|
||||||
|
*/
|
||||||
|
void trimString(char* str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查字符串是否为空
|
||||||
|
* @details 检查字符串是否为NULL、空字符串或只包含空白字符
|
||||||
|
* 使用isspace函数检查每个字符是否为空白字符
|
||||||
|
* @param str 要检查的字符串
|
||||||
|
* @return 如果字符串为空或只包含空白字符返回true,否则返回false
|
||||||
|
* @note 空白字符包括空格、制表符、换行符等
|
||||||
|
* @note 如果str为NULL,返回true
|
||||||
|
*/
|
||||||
|
bool isEmptyString(const char* str);
|
||||||
|
|
||||||
#endif // STRING_UTILS_H
|
#endif // STRING_UTILS_H
|
||||||
@@ -182,6 +182,9 @@ void addStudent()
|
|||||||
dataModified = true;
|
dataModified = true;
|
||||||
statsNeedUpdate = true;
|
statsNeedUpdate = true;
|
||||||
|
|
||||||
|
// 使统计缓存无效
|
||||||
|
invalidateCache();
|
||||||
|
|
||||||
// 显示添加成功信息
|
// 显示添加成功信息
|
||||||
displayAddedStudentInfo(&newStudent);
|
displayAddedStudentInfo(&newStudent);
|
||||||
|
|
||||||
@@ -243,6 +246,9 @@ void deleteStudent()
|
|||||||
dataModified = true;
|
dataModified = true;
|
||||||
statsNeedUpdate = true;
|
statsNeedUpdate = true;
|
||||||
|
|
||||||
|
// 使统计缓存无效
|
||||||
|
invalidateCache();
|
||||||
|
|
||||||
printSuccess("学生信息删除成功!");
|
printSuccess("学生信息删除成功!");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -449,6 +455,9 @@ static void handleStudentModification(Student *student)
|
|||||||
dataModified = true;
|
dataModified = true;
|
||||||
statsNeedUpdate = true;
|
statsNeedUpdate = true;
|
||||||
|
|
||||||
|
// 使统计缓存无效
|
||||||
|
invalidateCache();
|
||||||
|
|
||||||
printSuccess("学生信息修改成功!");
|
printSuccess("学生信息修改成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-3
@@ -11,19 +11,45 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 添加新学生
|
* @brief 添加新学生
|
||||||
* @details 交互式添加新学生信息,包括基本信息和课程成绩
|
* @details 交互式地添加新学生信息,包括基本信息和课程成绩
|
||||||
|
* 验证学号唯一性、姓名格式、年龄范围等
|
||||||
|
* 自动计算总分和平均分
|
||||||
|
* @note 会检查学生数量是否已达上限MAX_STUDENTS
|
||||||
|
* @note 学号必须唯一,不能与现有学生重复
|
||||||
|
* @warning 如果学生数量已满,会显示错误信息并返回
|
||||||
|
* @see MAX_STUDENTS, isValidStudentId(), isValidName()
|
||||||
*/
|
*/
|
||||||
void addStudent();
|
void addStudent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 删除学生
|
* @brief 删除学生
|
||||||
* @details 根据学号删除指定学生的所有信息
|
* @details 提供交互式界面删除指定学号的学生信息
|
||||||
|
* 包含确认机制,防止误删除操作
|
||||||
|
* @note 删除流程:
|
||||||
|
* 1. 输入要删除的学生学号
|
||||||
|
* 2. 查找并显示学生信息
|
||||||
|
* 3. 用户确认删除操作
|
||||||
|
* 4. 删除学生并重新排列数组
|
||||||
|
* 5. 更新数据修改和统计更新标志
|
||||||
|
* @warning 删除操作不可逆,请谨慎操作
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
*/
|
*/
|
||||||
void deleteStudent();
|
void deleteStudent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 修改学生信息
|
* @brief 修改学生信息
|
||||||
* @details 交互式修改学生的基本信息和课程成绩
|
* @details 提供交互式界面修改指定学生的各项信息
|
||||||
|
* 支持修改姓名、年龄、性别和课程成绩等信息
|
||||||
|
* @note 修改选项:
|
||||||
|
* 1. 修改姓名
|
||||||
|
* 2. 修改年龄
|
||||||
|
* 3. 修改性别
|
||||||
|
* 4. 修改课程成绩(包括修改现有成绩、添加新课程、删除课程)
|
||||||
|
* @note 课程成绩修改包含:
|
||||||
|
* - 修改现有课程成绩
|
||||||
|
* - 添加新课程
|
||||||
|
* - 删除课程
|
||||||
|
* @warning 修改课程信息后会自动重新计算总分和平均分
|
||||||
*/
|
*/
|
||||||
void modifyStudent();
|
void modifyStudent();
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,9 @@ void loadStudentsFromFile()
|
|||||||
|
|
||||||
// 更新统计信息
|
// 更新统计信息
|
||||||
statsNeedUpdate = true;
|
statsNeedUpdate = true;
|
||||||
|
|
||||||
|
// 使统计缓存无效
|
||||||
|
invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,12 +12,24 @@
|
|||||||
/**
|
/**
|
||||||
* @brief 从CSV文件加载学生数据
|
* @brief 从CSV文件加载学生数据
|
||||||
* @details 从STUDENTS_FILE指定的CSV文件中读取学生信息并加载到内存中
|
* @details 从STUDENTS_FILE指定的CSV文件中读取学生信息并加载到内存中
|
||||||
|
* 解析CSV格式数据,包括学号、姓名、年龄、性别、课程信息等
|
||||||
|
* 如果文件不存在,会初始化为空的学生列表
|
||||||
|
* @note 会跳过CSV文件的头部行,最多加载MAX_STUDENTS个学生
|
||||||
|
* @note 加载完成后会设置statsNeedUpdate标志为true
|
||||||
|
* @warning 如果CSV格式不正确,可能导致数据解析错误
|
||||||
|
* @see STUDENTS_FILE, MAX_STUDENTS, Student结构体
|
||||||
*/
|
*/
|
||||||
void loadStudentsFromFile();
|
void loadStudentsFromFile();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将学生数据保存到CSV文件
|
* @brief 将学生数据保存到CSV文件
|
||||||
* @details 将内存中的所有学生数据以CSV格式保存到STUDENTS_FILE文件中
|
* @details 将内存中的所有学生数据以CSV格式保存到STUDENTS_FILE文件中
|
||||||
|
* 包含完整的CSV头部和所有学生的详细信息
|
||||||
|
* 保存成功后会重置dataModified标志
|
||||||
|
* @note CSV格式包括:学号、姓名、年龄、性别、课程数量、各课程名称和成绩、总分、平均分
|
||||||
|
* @note 对于课程数量不足MAX_COURSES的学生,会用空值填充
|
||||||
|
* @warning 如果文件无法创建或写入,会显示错误信息
|
||||||
|
* @see STUDENTS_FILE, MAX_COURSES, dataModified
|
||||||
*/
|
*/
|
||||||
void saveStudentsToFile();
|
void saveStudentsToFile();
|
||||||
|
|
||||||
|
|||||||
+76
-6
@@ -13,12 +13,16 @@
|
|||||||
|
|
||||||
// 函数前向声明
|
// 函数前向声明
|
||||||
void displayStudentInfo(const Student *student);
|
void displayStudentInfo(const Student *student);
|
||||||
|
static int binarySearchByID(const char *studentID);
|
||||||
|
static void ensureSortedByID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 按学号查找学生
|
* @brief 按学号查找学生
|
||||||
* @details 根据用户输入的学号精确查找学生信息
|
* @details 根据用户输入的学号精确查找学生信息
|
||||||
|
* 使用二分搜索算法提高查找效率,时间复杂度O(log n)
|
||||||
* 找到后显示该学生的详细信息
|
* 找到后显示该学生的详细信息
|
||||||
* @note 查找方式:精确匹配学号
|
* @note 查找方式:精确匹配学号,使用二分搜索算法
|
||||||
|
* @note 算法优化:从线性搜索O(n)优化为二分搜索O(log n)
|
||||||
* @warning 如果没有学生数据或未找到匹配学生,将显示相应提示信息
|
* @warning 如果没有学生数据或未找到匹配学生,将显示相应提示信息
|
||||||
*/
|
*/
|
||||||
void searchStudentByID()
|
void searchStudentByID()
|
||||||
@@ -37,15 +41,18 @@ void searchStudentByID()
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
safeInputString("请输入学号", studentID, MAX_ID_LENGTH);
|
safeInputString("请输入学号", studentID, MAX_ID_LENGTH);
|
||||||
|
|
||||||
for (int i = 0; i < studentCount; i++)
|
// 确保数组按学号排序,以支持二分搜索
|
||||||
|
ensureSortedByID();
|
||||||
|
|
||||||
|
// 使用二分搜索查找学生
|
||||||
|
int index = binarySearchByID(studentID);
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
{
|
{
|
||||||
if (strcmp(students[i].studentID, studentID) == 0)
|
displayStudentInfo(&students[index]);
|
||||||
{
|
|
||||||
displayStudentInfo(&students[i]);
|
|
||||||
pauseSystem();
|
pauseSystem();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
printError("未找到该学号的学生!");
|
printError("未找到该学号的学生!");
|
||||||
pauseSystem();
|
pauseSystem();
|
||||||
@@ -174,3 +181,66 @@ void displayStudentInfo(const Student *student)
|
|||||||
printf("平均分: %.2f\n", student->averageScore);
|
printf("平均分: %.2f\n", student->averageScore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 确保学生数组按学号排序
|
||||||
|
* @details 检查学生数组是否按学号排序,如果没有则进行排序
|
||||||
|
* 用于支持二分搜索算法
|
||||||
|
* @note 只在需要时进行排序,避免不必要的性能开销
|
||||||
|
*/
|
||||||
|
static void ensureSortedByID()
|
||||||
|
{
|
||||||
|
// 检查是否已经按学号排序
|
||||||
|
bool isSorted = true;
|
||||||
|
for (int i = 0; i < studentCount - 1; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(students[i].studentID, students[i + 1].studentID) > 0)
|
||||||
|
{
|
||||||
|
isSorted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有排序,则按学号排序
|
||||||
|
if (!isSorted)
|
||||||
|
{
|
||||||
|
// 使用外部排序函数
|
||||||
|
extern void sortStudents(int criteria, int order);
|
||||||
|
sortStudents(SORT_BY_ID, SORT_ASCENDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 二分搜索按学号查找学生
|
||||||
|
* @details 使用二分搜索算法在已排序的学生数组中查找指定学号
|
||||||
|
* 时间复杂度O(log n),比线性搜索O(n)更高效
|
||||||
|
* @param studentID 要查找的学号
|
||||||
|
* @return 找到的学生索引,如果未找到返回-1
|
||||||
|
* @note 要求学生数组必须按学号排序
|
||||||
|
*/
|
||||||
|
static int binarySearchByID(const char *studentID)
|
||||||
|
{
|
||||||
|
int left = 0;
|
||||||
|
int right = studentCount - 1;
|
||||||
|
|
||||||
|
while (left <= right)
|
||||||
|
{
|
||||||
|
int mid = left + (right - left) / 2;
|
||||||
|
int cmp = strcmp(students[mid].studentID, studentID);
|
||||||
|
|
||||||
|
if (cmp == 0)
|
||||||
|
{
|
||||||
|
return mid; // 找到了
|
||||||
|
}
|
||||||
|
else if (cmp < 0)
|
||||||
|
{
|
||||||
|
left = mid + 1; // 在右半部分搜索
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
right = mid - 1; // 在左半部分搜索
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1; // 未找到
|
||||||
|
}
|
||||||
@@ -12,24 +12,42 @@
|
|||||||
/**
|
/**
|
||||||
* @brief 按学号查找学生
|
* @brief 按学号查找学生
|
||||||
* @details 根据用户输入的学号精确查找学生信息
|
* @details 根据用户输入的学号精确查找学生信息
|
||||||
|
* 找到后显示该学生的详细信息
|
||||||
|
* @note 查找方式:精确匹配学号
|
||||||
|
* @warning 如果没有学生数据或未找到匹配学生,将显示相应提示信息
|
||||||
*/
|
*/
|
||||||
void searchStudentByID();
|
void searchStudentByID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 按姓名查找学生
|
* @brief 按姓名查找学生
|
||||||
* @details 根据用户输入的姓名进行模糊查找学生信息
|
* @details 根据用户输入的姓名进行模糊查找学生信息
|
||||||
|
* 支持部分姓名匹配,显示所有匹配的学生详细信息
|
||||||
|
* @note 查找方式:模糊匹配(包含子字符串)
|
||||||
|
* @note 如果找到多个匹配学生,将全部显示
|
||||||
|
* @warning 如果没有学生数据或未找到匹配学生,将显示相应提示信息
|
||||||
*/
|
*/
|
||||||
void searchStudentByName();
|
void searchStudentByName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 显示所有学生信息
|
* @brief 显示所有学生信息
|
||||||
* @details 以表格形式显示系统中所有学生的基本信息
|
* @details 以表格形式显示系统中所有学生的基本信息
|
||||||
|
* 包括学号、姓名、年龄、性别、总分和平均分
|
||||||
|
* @note 显示格式:表格形式,便于查看和比较
|
||||||
|
* @note 显示内容:学号、姓名、年龄、性别、总分、平均分
|
||||||
|
* @note 同时显示总学生数统计
|
||||||
|
* @warning 如果没有学生数据,将显示警告信息
|
||||||
*/
|
*/
|
||||||
void displayAllStudents();
|
void displayAllStudents();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 显示单个学生详细信息
|
* @brief 显示单个学生详细信息
|
||||||
|
* @details 显示指定学生的完整详细信息,包括基本信息和所有课程成绩
|
||||||
* @param student 指向要显示信息的学生结构体的常量指针
|
* @param student 指向要显示信息的学生结构体的常量指针
|
||||||
|
* @note 显示内容:
|
||||||
|
* - 基本信息:学号、姓名、年龄、性别、课程数量
|
||||||
|
* - 课程成绩:每门课程的名称和分数
|
||||||
|
* - 统计信息:总分和平均分
|
||||||
|
* @warning 传入的student指针不能为NULL
|
||||||
*/
|
*/
|
||||||
void displayStudentInfo(const Student *student);
|
void displayStudentInfo(const Student *student);
|
||||||
|
|
||||||
|
|||||||
+76
-21
@@ -10,10 +10,74 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 比较函数 - 按学号排序
|
||||||
|
* @param a 指向第一个学生的指针
|
||||||
|
* @param b 指向第二个学生的指针
|
||||||
|
* @return 比较结果:负数表示a<b,0表示a=b,正数表示a>b
|
||||||
|
*/
|
||||||
|
static int compareByID(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const Student *studentA = (const Student *)a;
|
||||||
|
const Student *studentB = (const Student *)b;
|
||||||
|
int result = strcmp(studentA->studentID, studentB->studentID);
|
||||||
|
return (currentSortOrder == SORT_ASCENDING) ? result : -result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 比较函数 - 按姓名排序
|
||||||
|
* @param a 指向第一个学生的指针
|
||||||
|
* @param b 指向第二个学生的指针
|
||||||
|
* @return 比较结果:负数表示a<b,0表示a=b,正数表示a>b
|
||||||
|
*/
|
||||||
|
static int compareByName(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const Student *studentA = (const Student *)a;
|
||||||
|
const Student *studentB = (const Student *)b;
|
||||||
|
int result = strcmp(studentA->name, studentB->name);
|
||||||
|
return (currentSortOrder == SORT_ASCENDING) ? result : -result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 比较函数 - 按总分排序
|
||||||
|
* @param a 指向第一个学生的指针
|
||||||
|
* @param b 指向第二个学生的指针
|
||||||
|
* @return 比较结果:负数表示a<b,0表示a=b,正数表示a>b
|
||||||
|
*/
|
||||||
|
static int compareByTotalScore(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const Student *studentA = (const Student *)a;
|
||||||
|
const Student *studentB = (const Student *)b;
|
||||||
|
if (studentA->totalScore < studentB->totalScore)
|
||||||
|
return (currentSortOrder == SORT_ASCENDING) ? -1 : 1;
|
||||||
|
else if (studentA->totalScore > studentB->totalScore)
|
||||||
|
return (currentSortOrder == SORT_ASCENDING) ? 1 : -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 比较函数 - 按平均分排序
|
||||||
|
* @param a 指向第一个学生的指针
|
||||||
|
* @param b 指向第二个学生的指针
|
||||||
|
* @return 比较结果:负数表示a<b,0表示a=b,正数表示a>b
|
||||||
|
*/
|
||||||
|
static int compareByAverageScore(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const Student *studentA = (const Student *)a;
|
||||||
|
const Student *studentB = (const Student *)b;
|
||||||
|
if (studentA->averageScore < studentB->averageScore)
|
||||||
|
return (currentSortOrder == SORT_ASCENDING) ? -1 : 1;
|
||||||
|
else if (studentA->averageScore > studentB->averageScore)
|
||||||
|
return (currentSortOrder == SORT_ASCENDING) ? 1 : -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 排序学生信息
|
* @brief 排序学生信息
|
||||||
* @details 根据指定的排序依据和顺序对学生数组进行排序
|
* @details 根据指定的排序依据和顺序对学生数组进行排序
|
||||||
* 使用冒泡排序算法实现
|
* 使用qsort标准库函数实现,时间复杂度O(n log n)
|
||||||
* @param criteria 排序依据(SORT_BY_ID, SORT_BY_NAME, SORT_BY_TOTAL_SCORE, SORT_BY_AVERAGE_SCORE)
|
* @param criteria 排序依据(SORT_BY_ID, SORT_BY_NAME, SORT_BY_TOTAL_SCORE, SORT_BY_AVERAGE_SCORE)
|
||||||
* @param order 排序顺序(SORT_ASCENDING升序, SORT_DESCENDING降序)
|
* @param order 排序顺序(SORT_ASCENDING升序, SORT_DESCENDING降序)
|
||||||
* @note 排序依据选项:
|
* @note 排序依据选项:
|
||||||
@@ -21,7 +85,7 @@
|
|||||||
* - SORT_BY_NAME: 按姓名排序
|
* - SORT_BY_NAME: 按姓名排序
|
||||||
* - SORT_BY_TOTAL_SCORE: 按总分排序
|
* - SORT_BY_TOTAL_SCORE: 按总分排序
|
||||||
* - SORT_BY_AVERAGE_SCORE: 按平均分排序
|
* - SORT_BY_AVERAGE_SCORE: 按平均分排序
|
||||||
* @note 排序算法:冒泡排序(适合小规模数据)
|
* @note 排序算法:快速排序(qsort标准库函数,时间复杂度O(n log n))
|
||||||
* @note 排序完成后会设置dataModified标志
|
* @note 排序完成后会设置dataModified标志
|
||||||
*/
|
*/
|
||||||
void sortStudents(int criteria, int order)
|
void sortStudents(int criteria, int order)
|
||||||
@@ -29,36 +93,27 @@ void sortStudents(int criteria, int order)
|
|||||||
if (studentCount <= 1)
|
if (studentCount <= 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 使用冒泡排序
|
// 设置全局排序参数
|
||||||
for (int i = 0; i < studentCount - 1; i++)
|
currentSortCriteria = criteria;
|
||||||
{
|
currentSortOrder = order;
|
||||||
for (int j = 0; j < studentCount - 1 - i; j++)
|
|
||||||
{
|
|
||||||
bool shouldSwap = false;
|
|
||||||
|
|
||||||
|
// 选择对应的比较函数并使用qsort进行排序
|
||||||
switch (criteria)
|
switch (criteria)
|
||||||
{
|
{
|
||||||
case SORT_BY_ID:
|
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;
|
qsort(students, studentCount, sizeof(Student), compareByID);
|
||||||
break;
|
break;
|
||||||
case SORT_BY_NAME:
|
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;
|
qsort(students, studentCount, sizeof(Student), compareByName);
|
||||||
break;
|
break;
|
||||||
case SORT_BY_TOTAL_SCORE:
|
case SORT_BY_TOTAL_SCORE:
|
||||||
shouldSwap = (order == SORT_ASCENDING) ? students[j].totalScore > students[j + 1].totalScore : students[j].totalScore < students[j + 1].totalScore;
|
qsort(students, studentCount, sizeof(Student), compareByTotalScore);
|
||||||
break;
|
break;
|
||||||
case SORT_BY_AVERAGE_SCORE:
|
case SORT_BY_AVERAGE_SCORE:
|
||||||
shouldSwap = (order == SORT_ASCENDING) ? students[j].averageScore > students[j + 1].averageScore : students[j].averageScore < students[j + 1].averageScore;
|
qsort(students, studentCount, sizeof(Student), compareByAverageScore);
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
|
return; // 无效的排序依据
|
||||||
if (shouldSwap)
|
|
||||||
{
|
|
||||||
Student temp = students[j];
|
|
||||||
students[j] = students[j + 1];
|
|
||||||
students[j + 1] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dataModified = true;
|
dataModified = true;
|
||||||
|
|||||||
@@ -12,8 +12,16 @@
|
|||||||
/**
|
/**
|
||||||
* @brief 排序学生信息
|
* @brief 排序学生信息
|
||||||
* @details 根据指定的排序依据和顺序对学生数组进行排序
|
* @details 根据指定的排序依据和顺序对学生数组进行排序
|
||||||
|
* 使用冒泡排序算法实现
|
||||||
* @param criteria 排序依据(SORT_BY_ID, SORT_BY_NAME, SORT_BY_TOTAL_SCORE, SORT_BY_AVERAGE_SCORE)
|
* @param criteria 排序依据(SORT_BY_ID, SORT_BY_NAME, SORT_BY_TOTAL_SCORE, SORT_BY_AVERAGE_SCORE)
|
||||||
* @param order 排序顺序(SORT_ASCENDING升序, SORT_DESCENDING降序)
|
* @param order 排序顺序(SORT_ASCENDING升序, SORT_DESCENDING降序)
|
||||||
|
* @note 排序依据选项:
|
||||||
|
* - SORT_BY_ID: 按学号排序
|
||||||
|
* - SORT_BY_NAME: 按姓名排序
|
||||||
|
* - SORT_BY_TOTAL_SCORE: 按总分排序
|
||||||
|
* - SORT_BY_AVERAGE_SCORE: 按平均分排序
|
||||||
|
* @note 排序算法:冒泡排序(适合小规模数据)
|
||||||
|
* @note 排序完成后会设置dataModified标志
|
||||||
*/
|
*/
|
||||||
void sortStudents(int criteria, int order);
|
void sortStudents(int criteria, int order);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
#include "file_utils.h"
|
#include "file_utils.h"
|
||||||
#include "io_utils.h"
|
#include "io_utils.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "user_manage.h"
|
||||||
|
#include "student_io.h"
|
||||||
|
#include "statistical_analysis.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 初始化系统
|
* @brief 初始化系统
|
||||||
@@ -29,6 +32,15 @@ bool initializeSystem()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载用户数据
|
||||||
|
loadUsersFromFile();
|
||||||
|
|
||||||
|
// 加载学生数据
|
||||||
|
loadStudentsFromFile();
|
||||||
|
|
||||||
|
// 初始化统计缓存
|
||||||
|
initStatisticsCache();
|
||||||
|
|
||||||
printSuccess("系统初始化完成");
|
printSuccess("系统初始化完成");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-3
@@ -10,8 +10,34 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// 系统初始化和清理函数
|
// 系统初始化和清理函数
|
||||||
bool initializeSystem(); // 初始化系统
|
|
||||||
bool createDataDirectories(); // 创建数据目录
|
/**
|
||||||
void cleanupSystem(); // 清理系统资源
|
* @brief 初始化系统
|
||||||
|
* @details 执行系统启动时的初始化操作,包括创建必要的数据目录
|
||||||
|
* 调用createDataDirectories函数创建数据存储目录
|
||||||
|
* @return 如果初始化成功返回true,否则返回false
|
||||||
|
* @note 此函数应在程序启动时调用
|
||||||
|
* @note 如果初始化失败,会输出错误信息
|
||||||
|
*/
|
||||||
|
bool initializeSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建数据目录
|
||||||
|
* @details 创建程序运行所需的数据存储目录
|
||||||
|
* 目前创建"data"目录用于存储学生数据文件
|
||||||
|
* @return 如果目录创建成功或已存在返回true,否则返回false
|
||||||
|
* @note 如果目录已存在,函数仍返回true
|
||||||
|
* @note 可以根据需要扩展创建更多目录
|
||||||
|
*/
|
||||||
|
bool createDataDirectories();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清理系统资源
|
||||||
|
* @details 执行程序退出前的清理操作
|
||||||
|
* 目前主要输出清理完成的提示信息
|
||||||
|
* @note 此函数应在程序退出前调用
|
||||||
|
* @note 可以根据需要添加更多清理操作,如关闭文件、释放内存等
|
||||||
|
*/
|
||||||
|
void cleanupSystem();
|
||||||
|
|
||||||
#endif // SYSTEM_UTILS_H
|
#endif // SYSTEM_UTILS_H
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* @file types.h
|
||||||
|
* @brief 统一的数据类型定义文件
|
||||||
|
* @note 集中管理所有结构体定义,提高代码可维护性和一致性
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TYPES_H
|
||||||
|
#define TYPES_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// 核心数据结构
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 学生信息结构体
|
||||||
|
* @note 包含学生的基本信息、课程和成绩数据
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用户信息结构体
|
||||||
|
* @note 包含用户登录信息和权限设置
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
char username[MAX_USERNAME_LENGTH];
|
||||||
|
char password[MAX_PASSWORD_LENGTH];
|
||||||
|
bool isAdmin; // 是否为管理员
|
||||||
|
} User;
|
||||||
|
|
||||||
|
// 统计分析相关结构体
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 课程统计结构体
|
||||||
|
* @note 包含单门课程的统计信息
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int studentCount;
|
||||||
|
float maxScore;
|
||||||
|
float minScore;
|
||||||
|
float totalScore;
|
||||||
|
float averageScore;
|
||||||
|
float passRate;
|
||||||
|
} CourseStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 分数分布结构体
|
||||||
|
* @note 按分数段统计学生人数分布
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int excellent; // 90-100分
|
||||||
|
int good; // 80-89分
|
||||||
|
int medium; // 70-79分
|
||||||
|
int pass; // 60-69分
|
||||||
|
int fail; // 0-59分
|
||||||
|
} ScoreDistribution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 学生排名结构体
|
||||||
|
* @note 用于学生排名功能
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int studentIndex;
|
||||||
|
float averageScore;
|
||||||
|
float totalScore;
|
||||||
|
} StudentRank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 总体统计结构体
|
||||||
|
* @note 包含系统整体的统计信息
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int totalStudents;
|
||||||
|
int maleCount;
|
||||||
|
int femaleCount;
|
||||||
|
float averageAge;
|
||||||
|
float highestAverage;
|
||||||
|
float lowestAverage;
|
||||||
|
float overallAverageScore;
|
||||||
|
float standardDeviation;
|
||||||
|
int totalCourses;
|
||||||
|
float averageCoursesPerStudent;
|
||||||
|
} OverallStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 统计缓存结构体
|
||||||
|
* @note 用于缓存统计计算结果,提高性能
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
bool isValid; // 缓存是否有效
|
||||||
|
OverallStats overallStats; // 总体统计缓存
|
||||||
|
ScoreDistribution scoreDistribution; // 分数分布缓存
|
||||||
|
StudentRank rankings[MAX_STUDENTS]; // 排名缓存
|
||||||
|
int lastStudentCount; // 上次缓存时的学生数量
|
||||||
|
unsigned long lastDataHash; // 数据哈希值,用于检测数据变化
|
||||||
|
} StatisticsCache;
|
||||||
|
|
||||||
|
#endif // TYPES_H
|
||||||
+103
-8
@@ -1,15 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* @file user_manage.h
|
||||||
|
* @brief 用户管理实现文件
|
||||||
|
* @note 实现用户认证和管理功能函数声明
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef USER_MANAGE_H
|
#ifndef USER_MANAGE_H
|
||||||
#define USER_MANAGE_H
|
#define USER_MANAGE_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
// 用户认证与管理相关函数
|
/**
|
||||||
int loginSystem(); // 处理用户登录
|
* @brief 处理用户登录
|
||||||
void loadUsersFromFile(); // 从文件加载用户数据
|
* @details 提供用户登录验证功能,验证用户名和密码的正确性
|
||||||
void saveUsersToFile(); // 将用户数据保存到文件
|
* 登录成功后设置当前用户信息和管理员权限
|
||||||
void addUserAccount(); // 增加用户
|
* @return int 登录成功返回1,失败返回0
|
||||||
void deleteUserAccount(); // 删除用户
|
* @note 登录过程:
|
||||||
void modifyUserPassword(); // 修改用户密码
|
* 1. 获取用户输入的用户名和密码
|
||||||
void viewAllUsers(); // 查看所有用户
|
* 2. 遍历用户数组进行验证
|
||||||
|
* 3. 验证成功则设置currentUser和isCurrentUserAdmin
|
||||||
|
* @note 设置的全局变量:
|
||||||
|
* - currentUser: 当前登录用户名
|
||||||
|
* - isCurrentUserAdmin: 当前用户是否为管理员
|
||||||
|
*/
|
||||||
|
int loginSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从文件加载用户数据
|
||||||
|
* @details 从USERS_FILE文件中读取用户数据到内存
|
||||||
|
* 如果文件不存在,则创建默认的管理员和普通用户账户
|
||||||
|
* @note 文件格式:每行格式为 "username:password:isAdmin"
|
||||||
|
* 其中isAdmin为1表示管理员,0表示普通用户
|
||||||
|
* @note 默认账户:
|
||||||
|
* - 管理员:用户名admin,密码123456
|
||||||
|
* - 普通用户:用户名teacher,密码password
|
||||||
|
* @warning 如果文件格式错误,可能导致数据加载不完整
|
||||||
|
*/
|
||||||
|
void loadUsersFromFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将用户数据保存到文件
|
||||||
|
* @details 将内存中的用户数据写入到USERS_FILE文件中
|
||||||
|
* 采用文本格式存储,每个用户占一行
|
||||||
|
* @note 文件格式:每行格式为 "username:password:isAdmin"
|
||||||
|
* 其中isAdmin为1表示管理员,0表示普通用户
|
||||||
|
* @warning 如果文件无法打开,将显示错误信息
|
||||||
|
*/
|
||||||
|
void saveUsersToFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 增加用户账户
|
||||||
|
* @details 提供交互式界面添加新的用户账户
|
||||||
|
* 包括用户名唯一性检查、密码设置和用户类型选择
|
||||||
|
* @note 添加流程:
|
||||||
|
* 1. 检查用户数量是否达到上限
|
||||||
|
* 2. 输入新用户名并检查唯一性
|
||||||
|
* 3. 设置密码
|
||||||
|
* 4. 选择用户类型(普通用户/管理员)
|
||||||
|
* 5. 保存到文件并更新数据修改标志
|
||||||
|
* @warning 如果用户数量已达MAX_USERS上限,将拒绝添加
|
||||||
|
* @warning 如果用户名已存在,将拒绝添加
|
||||||
|
*/
|
||||||
|
void addUserAccount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 删除用户账户
|
||||||
|
* @details 提供交互式界面删除指定的用户账户
|
||||||
|
* 包含安全检查,防止删除当前登录用户和最后一个用户
|
||||||
|
* @note 删除限制:
|
||||||
|
* - 不能删除当前登录的用户
|
||||||
|
* - 系统至少需要保留一个用户账户
|
||||||
|
* @note 删除过程:
|
||||||
|
* 1. 输入要删除的用户名
|
||||||
|
* 2. 进行安全检查
|
||||||
|
* 3. 查找用户并删除
|
||||||
|
* 4. 重新排列用户数组
|
||||||
|
* 5. 保存到文件并更新数据修改标志
|
||||||
|
* @warning 删除操作不可逆,请谨慎操作
|
||||||
|
*/
|
||||||
|
void deleteUserAccount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 修改用户密码
|
||||||
|
* @details 提供交互式界面修改指定用户的密码
|
||||||
|
* 管理员可以修改任何用户的密码
|
||||||
|
* @note 修改流程:
|
||||||
|
* 1. 输入要修改密码的用户名
|
||||||
|
* 2. 查找用户是否存在
|
||||||
|
* 3. 输入新密码
|
||||||
|
* 4. 更新用户密码
|
||||||
|
* 5. 保存到文件并更新数据修改标志
|
||||||
|
* @warning 密码修改后立即生效,用户需要使用新密码登录
|
||||||
|
*/
|
||||||
|
void modifyUserPassword();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 查看所有用户
|
||||||
|
* @details 显示系统中所有用户的信息列表
|
||||||
|
* 包括用户名、用户类型和当前登录状态
|
||||||
|
* @note 显示内容:
|
||||||
|
* - 用户名
|
||||||
|
* - 用户类型(管理员/普通用户)
|
||||||
|
* - 状态(标识当前登录用户)
|
||||||
|
* - 总用户数统计
|
||||||
|
* @note 表格格式显示,便于查看和管理
|
||||||
|
* @warning 如果没有用户数据,将显示警告信息
|
||||||
|
*/
|
||||||
|
void viewAllUsers();
|
||||||
|
|
||||||
#endif // USER_MANAGE_H
|
#endif // USER_MANAGE_H
|
||||||
+47
-5
@@ -10,10 +10,52 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// 数据验证函数
|
// 数据验证函数
|
||||||
bool isValidScore(float score); // 验证成绩是否有效
|
|
||||||
bool isValidStudentId(const char* id); // 验证学号是否有效
|
/**
|
||||||
bool isValidName(const char* name); // 验证姓名是否有效
|
* @brief 验证成绩是否有效
|
||||||
bool isValidGender(char gender); // 验证性别是否有效
|
* @details 检查成绩是否在有效范围内(0-100分)
|
||||||
bool isValidAge(int age); // 验证年龄是否有效
|
* @param score 要验证的成绩值
|
||||||
|
* @return 如果成绩有效返回true,否则返回false
|
||||||
|
* @note 有效成绩范围为0.0到100.0(包含边界值)
|
||||||
|
*/
|
||||||
|
bool isValidScore(float score);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 验证学号是否有效
|
||||||
|
* @details 检查学号格式是否符合要求:非空且长度在合理范围内
|
||||||
|
* @param id 要验证的学号字符串
|
||||||
|
* @return 如果学号有效返回true,否则返回false
|
||||||
|
* @note 学号不能为空,长度必须在1到MAX_ID_LENGTH之间
|
||||||
|
* @warning 如果id为NULL,返回false
|
||||||
|
*/
|
||||||
|
bool isValidStudentId(const char* id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 验证姓名是否有效
|
||||||
|
* @details 检查姓名格式是否符合要求:非空且长度在合理范围内
|
||||||
|
* @param name 要验证的姓名字符串
|
||||||
|
* @return 如果姓名有效返回true,否则返回false
|
||||||
|
* @note 姓名不能为空,长度必须在1到MAX_NAME_LENGTH之间
|
||||||
|
* @warning 如果name为NULL,返回false
|
||||||
|
*/
|
||||||
|
bool isValidName(const char* name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 验证性别是否有效
|
||||||
|
* @details 检查性别是否为'M'(男)或'F'(女)
|
||||||
|
* @param gender 要验证的性别字符
|
||||||
|
* @return 如果性别有效返回true,否则返回false
|
||||||
|
* @note 只接受'M'或'F'两个值
|
||||||
|
*/
|
||||||
|
bool isValidGender(char gender);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 验证年龄是否有效
|
||||||
|
* @details 检查年龄是否在合理范围内
|
||||||
|
* @param age 要验证的年龄值
|
||||||
|
* @return 如果年龄有效返回true,否则返回false
|
||||||
|
* @note 有效年龄范围为MIN_AGE到MAX_AGE(包含边界值)
|
||||||
|
*/
|
||||||
|
bool isValidAge(int age);
|
||||||
|
|
||||||
#endif // VALIDATION_H
|
#endif // VALIDATION_H
|
||||||
Reference in New Issue
Block a user