Add files via upload

This commit is contained in:
2025-07-16 00:15:38 +08:00
committed by GitHub
parent 8f2d9ef4ad
commit dc652ffb31
30 changed files with 2875 additions and 752 deletions
+7 -13
View File
@@ -7,29 +7,23 @@ CFLAGS = -Wall -Wextra -std=c99 -g
TARGET = student_system TARGET = student_system
# 源文件 # 源文件
SOURCES = main.c globals.c auxiliary.c main_menu.c user_manage.c stu_data.c statistical_analysis.c SOURCES = 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
# 目标文件
OBJECTS = $(SOURCES:.c=.o)
# 头文件 # 头文件
HEADERS = config.h globals.h auxiliary.h main_menu.h user_manage.h stu.data.h statistical_analysis.h HEADERS = config.h globals.h main_menu.h user_manage.h core_handlers.h statistical_analysis.h student_io.h student_crud.h student_search.h student_sort.h io_utils.h validation.h string_utils.h file_utils.h math_utils.h system_utils.h
# 默认目标 # 默认目标
all: $(TARGET) all: $(TARGET)
# 链接目标文件 # 直接编译链接(不生成.o文件
$(TARGET): $(OBJECTS) $(TARGET): $(SOURCES) $(HEADERS)
$(CC) $(OBJECTS) -o $(TARGET) $(CC) $(CFLAGS) $(SOURCES) -o $(TARGET)
# 编译源文件
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c $< -o $@
# 清理编译文件 # 清理编译文件
clean: clean:
rm -f $(OBJECTS) $(TARGET) rm -f $(TARGET)
rm -f *.exe rm -f *.exe
rm -f *.o
# 创建必要的目录 # 创建必要的目录
setup: setup:
+4
View File
@@ -26,6 +26,10 @@
#define PASS_SCORE 60.0 // 及格分数 #define PASS_SCORE 60.0 // 及格分数
#define EXCELLENT_SCORE 90.0 // 优秀分数 #define EXCELLENT_SCORE 90.0 // 优秀分数
// 年龄相关配置
#define MIN_AGE 10 // 最小年龄
#define MAX_AGE 100 // 最大年龄
// 文件路径配置 // 文件路径配置
#define STUDENTS_FILE "data/students.csv" // 学生数据文件 #define STUDENTS_FILE "data/students.csv" // 学生数据文件
#define USERS_FILE "data/users.txt" // 用户数据文件 #define USERS_FILE "data/users.txt" // 用户数据文件
+199 -148
View File
@@ -4,151 +4,202 @@
* @note 实现主要的功能处理函数 * @note 实现主要的功能处理函数
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "core_handlers.h" #include "core_handlers.h"
#include "config.h" #include "config.h"
#include "globals.h" #include "globals.h"
#include "main_menu.h" #include "main_menu.h"
#include "user_manage.h" #include "user_manage.h"
#include "stu.data.h" #include "student_io.h"
#include "statistical_analysis.h" #include "student_crud.h"
#include "auxiliary.h" #include "student_search.h"
#include "student_sort.h"
/** #include "statistical_analysis.h"
* @brief 处理基本功能菜单 #include "io_utils.h"
*/
void handleBasicFunctions() /**
{ * @brief 处理基本功能菜单
int choice; * @details 显示并处理学生信息管理的基本功能菜单循环
do { * 提供学生信息的增删改查、排序等核心功能
clearScreen(); * 循环显示菜单直到用户选择返回主菜单
displayBasicFunctionsMenu(); * @note 包含的功能:
choice = safeInputInt("请选择功能", BASIC_BACK, BASIC_SORT_STUDENTS); * - 添加学生信息
* - 删除学生信息
switch (choice) { * - 修改学生信息
case BASIC_ADD_STUDENT: * - 按学号查找学生
addStudent(); * - 按姓名查找学生
break; * - 显示所有学生
case BASIC_DELETE_STUDENT: * - 学生信息排序
deleteStudent(); */
break; void handleBasicFunctions()
case BASIC_MODIFY_STUDENT: {
modifyStudent(); int choice;
break; do
case BASIC_SEARCH_BY_ID: {
searchStudentByID(); clearScreen();
break; displayBasicFunctionsMenu();
case BASIC_SEARCH_BY_NAME: choice = safeInputInt("请选择功能", BASIC_BACK, BASIC_SORT_STUDENTS);
searchStudentByName();
break; switch (choice)
case BASIC_DISPLAY_ALL: {
displayAllStudents(); case BASIC_ADD_STUDENT:
break; addStudent();
case BASIC_SORT_STUDENTS: break;
handleSortStudents(); case BASIC_DELETE_STUDENT:
break; deleteStudent();
case BASIC_BACK: break;
break; case BASIC_MODIFY_STUDENT:
default: modifyStudent();
printError("无效的选择!"); break;
pauseSystem(); case BASIC_SEARCH_BY_ID:
break; searchStudentByID();
} break;
} while (choice != BASIC_BACK); case BASIC_SEARCH_BY_NAME:
} searchStudentByName();
break;
/** case BASIC_DISPLAY_ALL:
* @brief 处理统计功能菜单 displayAllStudents();
*/ break;
void handleStatistics() { case BASIC_SORT_STUDENTS:
int choice; handleSortStudents();
do { break;
clearScreen(); case BASIC_BACK:
displayStatisticsMenu(); break;
choice = safeInputInt("请选择功能", STATS_BACK, STATS_OVERALL_ANALYSIS); default:
printError("无效的选择!");
switch (choice) { pauseSystem();
case STATS_COURSE_ANALYSIS: break;
displayCourseStatistics(); }
break; } while (choice != BASIC_BACK);
case STATS_SCORE_DISTRIBUTION: }
displayScoreDistribution();
break; /**
case STATS_SCORE_RANGES: * @brief 处理统计功能菜单
displayStudentRanking(); * @details 显示并处理统计分析功能菜单循环
break; * 提供各种学生成绩的统计分析功能
case STATS_OVERALL_ANALYSIS: * 循环显示菜单直到用户选择返回主菜单
displayOverallStatistics(); * @note 包含的功能:
break; * - 课程统计分析
case STATS_BACK: * - 成绩分布统计
break; * - 学生排名统计
default: * - 综合统计分析
printError("无效的选择!"); */
pauseSystem(); void handleStatistics()
break; {
} int choice;
} while (choice != STATS_BACK); do
} {
clearScreen();
/** displayStatisticsMenu();
* @brief 处理管理功能菜单 choice = safeInputInt("请选择功能", STATS_BACK, STATS_OVERALL_ANALYSIS);
*/
void handleAdminFunctions() { switch (choice)
int choice; {
do { case STATS_COURSE_ANALYSIS:
clearScreen(); displayCourseStatistics();
displayAdminMenu(); break;
choice = safeInputInt("请选择功能", ADMIN_BACK, ADMIN_VIEW_USERS); case STATS_SCORE_DISTRIBUTION:
displayScoreDistribution();
switch (choice) { break;
case ADMIN_ADD_USER: case STATS_SCORE_RANGES:
addUserAccount(); displayStudentRanking();
break; break;
case ADMIN_DELETE_USER: case STATS_OVERALL_ANALYSIS:
deleteUserAccount(); displayOverallStatistics();
break; break;
case ADMIN_MODIFY_PASSWORD: case STATS_BACK:
modifyUserPassword(); break;
break; default:
case ADMIN_VIEW_USERS: printError("无效的选择!");
viewAllUsers(); pauseSystem();
break; break;
case ADMIN_BACK: }
break; } while (choice != STATS_BACK);
default: }
printError("无效的选择!");
pauseSystem(); /**
break; * @brief 处理管理功能菜单
} * @details 显示并处理系统管理功能菜单循环,仅限管理员用户访问
} while (choice != ADMIN_BACK); * 提供用户账户管理的各项功能
} * 循环显示菜单直到用户选择返回主菜单
* @note 包含的功能:
/** * - 添加用户账户
* @brief 处理学生排序功能 * - 删除用户账户
*/ * - 修改用户密码
void handleSortStudents() { * - 查看所有用户
clearScreen(); * @warning 此函数仅应在验证用户为管理员后调用
printHeader("学生排序"); */
void handleAdminFunctions()
printf("排序依据:\n"); {
printf("1. 按学号排序\n"); int choice;
printf("2. 按姓名排序\n"); do
printf("3. 按总分排序\n"); {
printf("4. 按平均分排序\n"); clearScreen();
displayAdminMenu();
int criteria = safeInputInt("请选择排序依据", SORT_BY_ID, SORT_BY_AVERAGE_SCORE); choice = safeInputInt("请选择功能", ADMIN_BACK, ADMIN_VIEW_USERS);
printf("\n排序顺序:\n"); switch (choice)
printf("1. 升序\n"); {
printf("2. 降序\n"); case ADMIN_ADD_USER:
addUserAccount();
int order = safeInputInt("请选择排序顺序", SORT_ASCENDING, SORT_DESCENDING); break;
case ADMIN_DELETE_USER:
sortStudents(criteria, order); deleteUserAccount();
break;
printf("\n排序完成!\n"); case ADMIN_MODIFY_PASSWORD:
displayAllStudents(); modifyUserPassword();
} break;
case ADMIN_VIEW_USERS:
viewAllUsers();
break;
case ADMIN_BACK:
break;
default:
printError("无效的选择!");
pauseSystem();
break;
}
} while (choice != ADMIN_BACK);
}
/**
* @brief 处理学生排序功能
* @details 提供交互式的学生信息排序功能
* 用户可选择排序依据(学号、姓名、总分、平均分)和排序顺序(升序、降序)
* 排序完成后自动显示排序结果
* @note 排序依据选项:
* 1. 按学号排序
* 2. 按姓名排序
* 3. 按总分排序
* 4. 按平均分排序
* @note 排序顺序选项:
* 1. 升序
* 2. 降序
*/
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();
}
+67
View File
@@ -0,0 +1,67 @@
/**
* @file file_utils.c
* @brief 文件操作工具函数实现文件
* @note 实现文件和目录操作相关函数
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include <unistd.h>
#endif
#include "file_utils.h"
/**
* @brief 检查文件是否存在
* @details 使用access函数(Unix/Linux)或_access函数(Windows)检查文件是否存在且可读
* @param filename 要检查的文件路径
* @return 如果文件存在且可读返回true,否则返回false
* @note 函数只检查文件是否存在,不检查文件内容
* @warning 如果filename为NULL,返回false
*/
bool fileExists(const char *filename)
{
if (filename == NULL)
return false;
#ifdef _WIN32
return _access(filename, 0) == 0;
#else
return access(filename, F_OK) == 0;
#endif
}
/**
* @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)
{
if (path == NULL)
return false;
// 检查目录是否已存在
struct stat st = {0};
if (stat(path, &st) == 0)
{
return true; // 目录已存在
}
// 创建目录
#ifdef _WIN32
return _mkdir(path) == 0;
#else
return mkdir(path, 0755) == 0;
#endif
}
+16
View File
@@ -0,0 +1,16 @@
/**
* @file file_utils.h
* @brief 文件操作工具函数头文件
* @note 包含文件和目录操作相关函数声明
*/
#ifndef FILE_UTILS_H
#define FILE_UTILS_H
#include <stdbool.h>
// 文件操作函数
bool fileExists(const char* filename); // 检查文件是否存在
bool createDirectory(const char* path); // 创建目录
#endif // FILE_UTILS_H
-1
View File
@@ -9,7 +9,6 @@
#include <stdbool.h> #include <stdbool.h>
#include "config.h" #include "config.h"
#include "main_menu.h"
// 用户结构体定义 // 用户结构体定义
typedef struct { typedef struct {
+255
View File
@@ -0,0 +1,255 @@
/**
* @file io_utils.c
* @brief 输入输出工具函数实现文件
* @note 实现界面显示、用户输入等相关函数
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef _WIN32
#include <conio.h>
#endif
#include "io_utils.h"
#include "config.h"
#include "string_utils.h"
/**
* @brief 清理输入缓冲区
* @details 清除标准输入流中的所有剩余字符,直到遇到换行符或文件结束符
* 主要用于防止输入缓冲区中的残留字符影响后续输入操作
* @note 在使用scanf等函数后调用此函数可以避免输入问题
*/
void clearInputBuffer()
{
int c;
while ((c = getchar()) != '\n' && c != EOF)
;
}
/**
* @brief 暂停系统,等待用户按键
* @details 显示提示信息并等待用户按下任意键后继续执行
* 在Windows系统下使用_getch()函数,在其他系统下使用getchar()函数
* @note 用于在菜单操作完成后暂停,让用户有时间查看结果
* @warning 在非Windows系统下需要按回车键才能继续
*/
void pauseSystem()
{
printf("\n按任意键继续...");
#ifdef _WIN32
_getch();
#else
getchar();
#endif
printf("\n");
}
/**
* @brief 清屏
* @details 根据操作系统类型调用相应的清屏命令
* Windows系统使用"cls"命令,其他系统使用"clear"命令
* @note 用于清除终端屏幕内容,提供更好的用户界面体验
* @warning 依赖于系统命令,在某些受限环境下可能无法正常工作
*/
void clearScreen()
{
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
/**
* @brief 打印分隔线
* @details 输出一行由等号组成的分隔线,用于美化界面显示
* @note 分隔线长度为40个字符,用于分隔不同的界面区域
*/
void printSeparator()
{
printf("========================================\n");
}
/**
* @brief 打印标题头
* @details 以美观的格式显示标题,标题上下各有一条分隔线
* @param title 要显示的标题文本,不能为NULL
* @note 标题会居中显示,前面有10个空格的缩进
* @warning 如果title为NULL,可能导致程序崩溃
*/
void printHeader(const char *title)
{
printSeparator();
printf(" %s\n", title);
printSeparator();
}
/**
* @brief 安全输入整数
* @details 提供安全的整数输入功能,包含范围验证和错误处理
* 使用fgets和sscanf组合避免缓冲区溢出,循环直到获得有效输入
* @param prompt 显示给用户的提示信息
* @param min 允许输入的最小值(包含)
* @param max 允许输入的最大值(包含)
* @return 返回用户输入的有效整数
* @note 函数会一直循环直到用户输入有效的整数
* @note 自动显示输入范围提示
* @warning 如果prompt为NULLprintf可能出现问题
*/
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 安全输入浮点数
* @details 提供安全的浮点数输入功能,包含范围验证和错误处理
* 使用fgets和sscanf组合避免缓冲区溢出,循环直到获得有效输入
* @param prompt 显示给用户的提示信息
* @param min 允许输入的最小值(包含)
* @param max 允许输入的最大值(包含)
* @return 返回用户输入的有效浮点数
* @note 函数会一直循环直到用户输入有效的浮点数
* @note 自动显示输入范围提示,精度为小数点后1位
* @warning 如果prompt为NULLprintf可能出现问题
*/
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 安全输入字符串
* @details 提供安全的字符串输入功能,包含空值检查和自动去除首尾空格
* 使用fgets避免缓冲区溢出,自动移除换行符并处理空白字符
* @param prompt 显示给用户的提示信息
* @param buffer 存储输入字符串的缓冲区
* @param maxLen 缓冲区的最大长度(包含终止符)
* @note 函数会一直循环直到用户输入非空字符串
* @note 自动移除输入字符串的首尾空白字符
* @warning 如果buffer为NULL或maxLen<=0,可能导致程序崩溃
*/
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 彩色输出
* @details 使用ANSI转义序列在终端中输出彩色文本
* 输出格式为:颜色代码 + 文本 + 重置代码
* @param text 要输出的文本内容
* @param color ANSI颜色代码字符串(如COLOR_RED、COLOR_GREEN等)
* @note 输出后会自动重置颜色为默认值
* @warning 如果终端不支持ANSI转义序列,可能显示乱码
*/
void printColored(const char *text, const char *color)
{
printf("%s%s%s", color, text, COLOR_RESET);
}
/**
* @brief 成功消息
* @details 以绿色显示成功消息,用于提示操作成功完成
* @param message 要显示的成功消息文本
* @note 消息会以绿色显示,并在末尾自动添加换行符
*/
void printSuccess(const char *message)
{
printColored(message, COLOR_GREEN);
printf("\n");
}
/**
* @brief 错误消息
* @details 以红色显示错误消息,用于提示操作失败或出现错误
* @param message 要显示的错误消息文本
* @note 消息会以红色显示,并在末尾自动添加换行符
*/
void printError(const char *message)
{
printColored(message, COLOR_RED);
printf("\n");
}
/**
* @brief 警告消息
* @details 以黄色显示警告消息,用于提示需要注意的情况
* @param message 要显示的警告消息文本
* @note 消息会以黄色显示,并在末尾自动添加换行符
*/
void printWarning(const char *message)
{
printColored(message, COLOR_YELLOW);
printf("\n");
}
/**
* @brief 信息消息
* @details 以青色显示信息消息,用于提示一般性信息
* @param message 要显示的信息消息文本
* @note 消息会以青色显示,并在末尾自动添加换行符
*/
void printInfo(const char *message)
{
printColored(message, COLOR_CYAN);
printf("\n");
}
+31
View File
@@ -0,0 +1,31 @@
/**
* @file io_utils.h
* @brief 输入输出工具函数头文件
* @note 包含界面显示、用户输入等相关函数声明
*/
#ifndef IO_UTILS_H
#define IO_UTILS_H
#include <stdbool.h>
// 界面显示函数
void clearInputBuffer(); // 清理输入缓冲区
void pauseSystem(); // 暂停系统,等待用户按键
void clearScreen(); // 清屏
void printSeparator(); // 打印分隔线
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); // 安全输入字符串
// 颜色输出函数
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 // IO_UTILS_H
+17 -12
View File
@@ -19,24 +19,29 @@
#include "globals.h" #include "globals.h"
#include "main_menu.h" #include "main_menu.h"
#include "user_manage.h" #include "user_manage.h"
#include "stu.data.h"
#include "statistical_analysis.h" #include "statistical_analysis.h"
#include "auxiliary.h" #include "io_utils.h"
#include "system_utils.h"
#include "core_handlers.h" #include "core_handlers.h"
#include "student_io.h"
/** /**
* @brief 主程序入口 * @brief 主程序入口
* @param argc 命令行参数个数 * @details 学生成绩管理系统的主入口函数,负责系统初始化、用户登录验证、
* @param argv 命令行参数数组 * 主菜单循环处理和系统清理等核心流程
* @return 程序退出状态码 * 程序流程:设置编码 -> 系统初始化 -> 用户登录 -> 主菜单循环 -> 数据保存 -> 系统清理
* @note 系统中有两个用户: * @param argc 命令行参数个数(当前未使用)
1. admin - 密码是 123456 * @param argv 命令行参数数组(当前未使用)
2. teacher - 密码是 password * @return 程序退出状态码:0表示正常退出,-1表示异常退出
* @note * @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 * 1. admin - 密码:123456(管理员权限)
.\student_system.exe * 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, char *argv[]) int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{ {
// 设置控制台编码为UTF-8 // 设置控制台编码为UTF-8
#ifdef _WIN32 #ifdef _WIN32
+52 -17
View File
@@ -7,14 +7,21 @@
#include <stdio.h> #include <stdio.h>
#include "main_menu.h" #include "main_menu.h"
#include "config.h" #include "config.h"
#include "auxiliary.h" #include "io_utils.h"
#include "globals.h" #include "globals.h"
/** /**
* @brief 显示主菜单 * @brief 显示主菜单
*/ * @details 显示学生成绩管理系统的主菜单界面,包括当前用户信息和可用功能选项
void displayMainMenu() * 根据用户权限动态显示菜单项(管理员可看到系统管理功能)
{ * @note 菜单选项:
* 1. 基本功能管理(所有用户)
* 2. 统计分析功能(所有用户)
* 3. 系统管理功能(仅管理员)
* 0. 退出系统
*/
void displayMainMenu()
{
printHeader("学生成绩管理系统 - 主菜单"); printHeader("学生成绩管理系统 - 主菜单");
printf("\n"); printf("\n");
printf("当前用户: %s %s\n", currentUser, printf("当前用户: %s %s\n", currentUser,
@@ -32,10 +39,21 @@
} }
/** /**
* @brief 显示基本功能菜单 * @brief 显示基本功能菜单
*/ * @details 显示学生信息管理的基本功能菜单,包括增删改查和排序功能
void displayBasicFunctionsMenu() * 同时显示当前系统中的学生总数
{ * @note 菜单功能:
* 1. 添加学生信息
* 2. 删除学生信息
* 3. 修改学生信息
* 4. 按学号查找学生
* 5. 按姓名查找学生
* 6. 显示所有学生
* 7. 学生信息排序
* 0. 返回主菜单
*/
void displayBasicFunctionsMenu()
{
printHeader("基本功能管理"); printHeader("基本功能管理");
printf("\n"); printf("\n");
printf("1. 添加学生信息\n"); printf("1. 添加学生信息\n");
@@ -53,10 +71,18 @@
} }
/** /**
* @brief 显示统计功能菜单 * @brief 显示统计功能菜单
*/ * @details 显示统计分析功能菜单,提供各种数据统计和分析选项
void displayStatisticsMenu() * 显示当前学生总数和系统平均分(如果有学生数据)
{ * @note 菜单功能:
* 1. 课程统计分析
* 2. 成绩分布统计
* 3. 分数段统计
* 4. 综合统计分析
* 0. 返回主菜单
*/
void displayStatisticsMenu()
{
printHeader("统计分析功能"); printHeader("统计分析功能");
printf("\n"); printf("\n");
printf("1. 课程统计分析\n"); printf("1. 课程统计分析\n");
@@ -74,10 +100,19 @@
} }
/** /**
* @brief 显示管理功能菜单 * @brief 显示管理功能菜单
*/ * @details 显示系统管理功能菜单,仅管理员可访问
void displayAdminMenu() * 提供用户账户管理功能,显示当前用户总数
{ * @note 菜单功能:
* 1. 添加用户账户
* 2. 删除用户账户
* 3. 修改用户密码
* 4. 查看所有用户
* 0. 返回主菜单
* @warning 此菜单仅限管理员用户访问
*/
void displayAdminMenu()
{
printHeader("系统管理功能"); printHeader("系统管理功能");
printf("\n"); printf("\n");
printf("1. 添加用户账户\n"); printf("1. 添加用户账户\n");
+31
View File
@@ -0,0 +1,31 @@
/**
* @file math_utils.c
* @brief 数学计算工具函数实现文件
* @note 实现数学计算相关函数
*/
#include "math_utils.h"
/**
* @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)
{
if (count <= 0)
return 0.0;
float sum = 0.0;
for (int i = 0; i < count; i++)
{
sum += scores[i];
}
return sum / count;
}
+13
View File
@@ -0,0 +1,13 @@
/**
* @file math_utils.h
* @brief 数学计算工具函数头文件
* @note 包含数学计算相关函数声明
*/
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
// 数学计算函数
float calculateAverage(float scores[], int count); // 计算平均值
#endif // MATH_UTILS_H
+648 -482
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -8,7 +8,6 @@
#define STATISTICAL_ANALYSIS_H #define STATISTICAL_ANALYSIS_H
#include "config.h" #include "config.h"
#include "main_menu.h"
// 课程统计结构体 // 课程统计结构体
typedef struct { typedef struct {
+76
View File
@@ -0,0 +1,76 @@
/**
* @file string_utils.c
* @brief 字符串处理工具函数实现文件
* @note 实现字符串操作相关函数
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "string_utils.h"
/**
* @brief 去除字符串首尾空白字符
* @details 移除字符串开头和结尾的空格、制表符、换行符等空白字符
* 使用双指针技术,从两端向中间处理,原地修改字符串
* @param str 要处理的字符串,函数会直接修改此字符串
* @note 函数会直接修改传入的字符串,不会分配新内存
* @note 如果整个字符串都是空白字符,结果将是空字符串
* @warning 如果str为NULL,可能导致程序崩溃
*/
void trimString(char *str)
{
if (str == NULL)
return;
// 去除开头的空白字符
char *start = str;
while (isspace((unsigned char)*start))
{
start++;
}
// 如果字符串全是空白字符
if (*start == '\0')
{
str[0] = '\0';
return;
}
// 去除结尾的空白字符
char *end = str + strlen(str) - 1;
while (end > start && isspace((unsigned char)*end))
{
end--;
}
// 移动字符串到开头
int len = end - start + 1;
memmove(str, start, len);
str[len] = '\0';
}
/**
* @brief 检查字符串是否为空
* @details 检查字符串是否为NULL、空字符串或只包含空白字符
* 使用isspace函数检查每个字符是否为空白字符
* @param str 要检查的字符串
* @return 如果字符串为空或只包含空白字符返回true,否则返回false
* @note 空白字符包括空格、制表符、换行符等
* @note 如果str为NULL,返回true
*/
bool isEmptyString(const char *str)
{
if (str == NULL)
return true;
while (*str)
{
if (!isspace((unsigned char)*str))
{
return false;
}
str++;
}
return true;
}
+16
View File
@@ -0,0 +1,16 @@
/**
* @file string_utils.h
* @brief 字符串处理工具函数头文件
* @note 包含字符串操作相关函数声明
*/
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include <stdbool.h>
// 字符串处理函数
void trimString(char* str); // 去除字符串首尾空白字符
bool isEmptyString(const char* str); // 检查字符串是否为空
#endif // STRING_UTILS_H
+499
View File
@@ -0,0 +1,499 @@
/**
* @file student_crud.c
* @brief 学生数据增删改操作实现
* @note 负责学生信息的添加、删除、修改等CRUD操作
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "globals.h"
#include "io_utils.h"
#include "validation.h"
#include "statistical_analysis.h"
/**
* @brief 输入并验证学生学号
* @details 循环输入学号直到格式正确且不重复
* @param student 指向要填充的学生结构体的指针
*/
static void inputStudentID(Student *student)
{
while (1)
{
safeInputString("请输入学号", student->studentID, MAX_ID_LENGTH);
if (!isValidStudentId(student->studentID))
{
printError("学号格式无效!");
continue;
}
// 检查学号是否已存在
bool exists = false;
for (int i = 0; i < studentCount; i++)
{
if (strcmp(students[i].studentID, student->studentID) == 0)
{
printError("学号已存在!");
exists = true;
break;
}
}
if (!exists)
break;
}
}
/**
* @brief 输入并验证学生基本信息
* @details 输入学生的姓名、年龄和性别信息
* @param student 指向要填充的学生结构体的指针
*/
static void inputBasicInfo(Student *student)
{
// 输入姓名
while (1)
{
safeInputString("请输入姓名", student->name, MAX_NAME_LENGTH);
if (isValidName(student->name))
break;
printError("姓名格式无效!");
}
// 输入年龄
student->age = safeInputInt("请输入年龄", 10, 100);
// 输入性别
while (1)
{
printf("请输入性别 (M/F): ");
char gender;
scanf(" %c", &gender);
clearInputBuffer();
if (isValidGender(gender))
{
student->gender = gender;
break;
}
printError("性别输入无效!请输入 M 或 F");
}
}
/**
* @brief 输入学生课程信息
* @details 循环输入课程名称和成绩,支持添加多门课程
* @param student 指向要填充的学生结构体的指针
*/
static void inputCourseInfo(Student *student)
{
printf("\n开始输入课程信息:\n");
student->courseCount = 0;
while (student->courseCount < MAX_COURSES)
{
printf("\n第 %d 门课程:\n", student->courseCount + 1);
safeInputString("课程名称",
student->courses[student->courseCount],
MAX_COURSE_NAME_LENGTH);
student->scores[student->courseCount] =
safeInputFloat("课程分数", MIN_SCORE, MAX_SCORE);
student->courseCount++;
if (student->courseCount < MAX_COURSES)
{
printf("\n是否继续添加课程?(y/n): ");
char choice;
scanf(" %c", &choice);
clearInputBuffer();
if (choice != 'y' && choice != 'Y')
{
break;
}
}
}
}
/**
* @brief 显示添加成功的学生信息
* @details 显示新添加学生的基本信息和统计数据
* @param student 指向学生结构体的指针
*/
static void displayAddedStudentInfo(const Student *student)
{
printSuccess("学生信息添加成功!");
printf("学号: %s\n", student->studentID);
printf("姓名: %s\n", student->name);
printf("总分: %.2f\n", student->totalScore);
printf("平均分: %.2f\n", student->averageScore);
}
/**
* @brief 添加学生信息
* @details 交互式地添加新学生信息,包括基本信息和课程成绩
* 验证学号唯一性、姓名格式、年龄范围等
* 自动计算总分和平均分
* @note 会检查学生数量是否已达上限MAX_STUDENTS
* @note 学号必须唯一,不能与现有学生重复
* @warning 如果学生数量已满,会显示错误信息并返回
* @see MAX_STUDENTS, isValidStudentId(), isValidName()
*/
void addStudent()
{
clearScreen();
printHeader("添加学生信息");
if (studentCount >= MAX_STUDENTS)
{
printError("学生数量已达上限!");
pauseSystem();
return;
}
Student newStudent;
memset(&newStudent, 0, sizeof(Student));
printf("\n");
// 输入学号
inputStudentID(&newStudent);
// 输入基本信息
inputBasicInfo(&newStudent);
// 输入课程信息
inputCourseInfo(&newStudent);
// 计算总分和平均分
calculateStudentStats(&newStudent);
// 添加到数组
students[studentCount] = newStudent;
studentCount++;
// 标记数据已修改
dataModified = true;
statsNeedUpdate = true;
// 显示添加成功信息
displayAddedStudentInfo(&newStudent);
pauseSystem();
}
/**
* @brief 删除学生信息
* @details 提供交互式界面删除指定学号的学生信息
* 包含确认机制,防止误删除操作
* @note 删除流程:
* 1. 输入要删除的学生学号
* 2. 查找并显示学生信息
* 3. 用户确认删除操作
* 4. 删除学生并重新排列数组
* 5. 更新数据修改和统计更新标志
* @warning 删除操作不可逆,请谨慎操作
* @warning 如果没有学生数据,将显示警告信息
*/
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 显示学生基本信息
* @details 显示学生的学号、姓名、年龄、性别等基本信息
* @param student 指向学生结构体的指针
*/
static void displayStudentBasicInfo(const Student *student)
{
printf("\n找到学生信息:\n");
printf("学号: %s\n", student->studentID);
printf("姓名: %s\n", student->name);
printf("年龄: %d\n", student->age);
printf("性别: %c\n", student->gender);
}
/**
* @brief 修改学生性别
* @details 循环输入直到获得有效的性别值
* @param student 指向学生结构体的指针
*/
static void modifyStudentGender(Student *student)
{
while (1)
{
printf("请输入新性别 (M/F): ");
char gender;
scanf(" %c", &gender);
clearInputBuffer();
if (isValidGender(gender))
{
student->gender = gender;
break;
}
printError("性别输入无效!");
}
}
/**
* @brief 修改现有课程成绩
* @details 选择并修改学生的现有课程成绩
* @param student 指向学生结构体的指针
*/
static void modifyExistingCourse(Student *student)
{
if (student->courseCount == 0)
{
printWarning("该学生没有课程记录!");
return;
}
int courseIndex = safeInputInt("请选择要修改的课程", 1, student->courseCount) - 1;
student->scores[courseIndex] = safeInputFloat("新成绩", MIN_SCORE, MAX_SCORE);
}
/**
* @brief 添加新课程
* @details 为学生添加新的课程和成绩
* @param student 指向学生结构体的指针
*/
static void addNewCourse(Student *student)
{
if (student->courseCount >= MAX_COURSES)
{
printWarning("课程数量已达上限!");
return;
}
safeInputString("课程名称", student->courses[student->courseCount], MAX_COURSE_NAME_LENGTH);
student->scores[student->courseCount] = safeInputFloat("课程成绩", MIN_SCORE, MAX_SCORE);
student->courseCount++;
}
/**
* @brief 删除课程
* @details 删除学生的指定课程和成绩
* @param student 指向学生结构体的指针
*/
static void deleteCourse(Student *student)
{
if (student->courseCount == 0)
{
printWarning("该学生没有课程记录!");
return;
}
int courseIndex = safeInputInt("请选择要删除的课程", 1, student->courseCount) - 1;
// 移动数组元素
for (int k = courseIndex; k < student->courseCount - 1; k++)
{
strcpy(student->courses[k], student->courses[k + 1]);
student->scores[k] = student->scores[k + 1];
}
student->courseCount--;
}
/**
* @brief 显示课程列表
* @details 显示学生的所有课程和成绩
* @param student 指向学生结构体的指针
*/
static void displayCourseList(const Student *student)
{
printf("\n当前课程列表:\n");
for (int j = 0; j < student->courseCount; j++)
{
printf("%d. %s: %.2f\n", j + 1, student->courses[j], student->scores[j]);
}
}
/**
* @brief 修改课程信息
* @details 提供课程修改的子菜单,包括修改、添加、删除课程
* @param student 指向学生结构体的指针
* @return true 如果进行了课程修改,false 如果没有修改
*/
static bool modifyCourseInfo(Student *student)
{
displayCourseList(student);
printf("\n修改选项:\n");
printf("1. 修改现有课程成绩\n");
printf("2. 添加新课程\n");
printf("3. 删除课程\n");
printf("0. 返回\n");
int courseChoice = safeInputInt("请选择操作", 0, 3);
switch (courseChoice)
{
case 1: // 修改现有课程成绩
modifyExistingCourse(student);
return true;
case 2: // 添加新课程
addNewCourse(student);
return true;
case 3: // 删除课程
deleteCourse(student);
return true;
case 0:
return false;
}
return false;
}
/**
* @brief 处理学生信息修改
* @details 处理找到学生后的修改操作
* @param student 指向学生结构体的指针
*/
static void handleStudentModification(Student *student)
{
displayStudentBasicInfo(student);
printf("\n修改选项:\n");
printf("1. 修改姓名\n");
printf("2. 修改年龄\n");
printf("3. 修改性别\n");
printf("4. 修改课程成绩\n");
printf("0. 返回\n");
int choice = safeInputInt("请选择修改项", 0, 4);
bool courseModified = false;
switch (choice)
{
case 1: // 修改姓名
safeInputString("请输入新姓名", student->name, MAX_NAME_LENGTH);
break;
case 2: // 修改年龄
student->age = safeInputInt("请输入新年龄", 10, 100);
break;
case 3: // 修改性别
modifyStudentGender(student);
break;
case 4: // 修改课程成绩
courseModified = modifyCourseInfo(student);
break;
case 0:
return;
}
if (courseModified)
{
calculateStudentStats(student);
}
dataModified = true;
statsNeedUpdate = true;
printSuccess("学生信息修改成功!");
}
/**
* @brief 修改学生信息
* @details 提供交互式界面修改指定学生的各项信息
* 支持修改姓名、年龄、性别和课程成绩等信息
* @note 修改选项:
* 1. 修改姓名
* 2. 修改年龄
* 3. 修改性别
* 4. 修改课程成绩(包括修改现有成绩、添加新课程、删除课程)
* @note 课程成绩修改包含:
* - 修改现有课程成绩
* - 添加新课程
* - 删除课程
* @warning 修改课程信息后会自动重新计算总分和平均分
*/
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)
{
handleStudentModification(&students[i]);
pauseSystem();
return;
}
}
printError("未找到该学号的学生!");
pauseSystem();
}
+30
View File
@@ -0,0 +1,30 @@
/**
* @file student_crud.h
* @brief 学生数据增删改操作头文件
* @note 声明学生数据的创建、删除、修改等CRUD功能
*/
#ifndef STUDENT_CRUD_H
#define STUDENT_CRUD_H
#include "config.h"
/**
* @brief 添加新学生
* @details 交互式添加新学生信息,包括基本信息和课程成绩
*/
void addStudent();
/**
* @brief 删除学生
* @details 根据学号删除指定学生的所有信息
*/
void deleteStudent();
/**
* @brief 修改学生信息
* @details 交互式修改学生的基本信息和课程成绩
*/
void modifyStudent();
#endif // STUDENT_CRUD_H
+234
View File
@@ -0,0 +1,234 @@
/**
* @file student_io.c
* @brief 学生数据文件输入输出操作实现
* @note 负责学生数据的文件读写、CSV解析等功能
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "globals.h"
#include "io_utils.h"
#include "string_utils.h"
#include "statistical_analysis.h"
/**
* @brief 解析CSV行中的基本学生信息
* @details 从CSV行中解析学号、姓名、年龄、性别等基本信息
* @param student 指向要填充的学生结构体的指针
* @param token 当前CSV token指针的指针
* @return true 解析成功,false 解析失败
*/
static bool parseBasicStudentInfo(Student *student, char **token)
{
// 学号
if (*token == NULL) return false;
strncpy(student->studentID, *token, MAX_ID_LENGTH - 1);
// 姓名
*token = strtok(NULL, ",");
if (*token == NULL) return false;
strncpy(student->name, *token, MAX_NAME_LENGTH - 1);
// 年龄
*token = strtok(NULL, ",");
if (*token == NULL) return false;
student->age = atoi(*token);
// 性别
*token = strtok(NULL, ",");
if (*token == NULL) return false;
student->gender = (*token)[0];
// 课程数量
*token = strtok(NULL, ",");
if (*token == NULL) return false;
student->courseCount = atoi(*token);
return true;
}
/**
* @brief 解析CSV行中的课程信息
* @details 从CSV行中解析课程名称和成绩信息
* @param student 指向要填充的学生结构体的指针
* @param token 当前CSV token指针的指针
*/
static void parseCourseInfo(Student *student, char **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);
}
}
/**
* @brief 解析CSV行中的统计信息
* @details 从CSV行中解析总分和平均分信息
* @param student 指向要填充的学生结构体的指针
* @param token 当前CSV token指针的指针
*/
static void parseStatisticsInfo(Student *student, char **token)
{
// 总分
*token = strtok(NULL, ",");
if (*token != NULL)
{
student->totalScore = atof(*token);
}
// 平均分
*token = strtok(NULL, ",");
if (*token != NULL)
{
student->averageScore = atof(*token);
}
}
/**
* @brief 解析单行CSV学生数据
* @details 解析一行CSV数据并填充学生结构体
* @param line CSV行数据
* @param student 指向要填充的学生结构体的指针
* @return true 解析成功,false 解析失败
*/
static bool parseStudentLine(char *line, Student *student)
{
memset(student, 0, sizeof(Student));
// 解析CSV行
char *token = strtok(line, ",");
if (token == NULL) return false;
// 解析基本信息
if (!parseBasicStudentInfo(student, &token)) return false;
// 解析课程信息
parseCourseInfo(student, &token);
// 解析统计信息
parseStatisticsInfo(student, &token);
return true;
}
/**
* @brief 从CSV文件加载学生数据
* @details 从STUDENTS_FILE指定的CSV文件中读取学生信息并加载到内存中
* 解析CSV格式数据,包括学号、姓名、年龄、性别、课程信息等
* 如果文件不存在,会初始化为空的学生列表
* @note 会跳过CSV文件的头部行,最多加载MAX_STUDENTS个学生
* @note 加载完成后会设置statsNeedUpdate标志为true
* @warning 如果CSV格式不正确,可能导致数据解析错误
* @see STUDENTS_FILE, MAX_STUDENTS, Student结构体
*/
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];
if (parseStudentLine(line, student))
{
studentCount++;
}
}
fclose(file);
// 更新统计信息
statsNeedUpdate = true;
}
/**
* @brief 将学生数据保存到CSV文件
* @details 将内存中的所有学生数据以CSV格式保存到STUDENTS_FILE文件中
* 包含完整的CSV头部和所有学生的详细信息
* 保存成功后会重置dataModified标志
* @note CSV格式包括:学号、姓名、年龄、性别、课程数量、各课程名称和成绩、总分、平均分
* @note 对于课程数量不足MAX_COURSES的学生,会用空值填充
* @warning 如果文件无法创建或写入,会显示错误信息
* @see STUDENTS_FILE, MAX_COURSES, dataModified
*/
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文件!");
}
+24
View File
@@ -0,0 +1,24 @@
/**
* @file student_io.h
* @brief 学生数据文件输入输出操作头文件
* @note 声明学生数据的文件读写、CSV解析等功能
*/
#ifndef STUDENT_IO_H
#define STUDENT_IO_H
#include "config.h"
/**
* @brief 从CSV文件加载学生数据
* @details 从STUDENTS_FILE指定的CSV文件中读取学生信息并加载到内存中
*/
void loadStudentsFromFile();
/**
* @brief 将学生数据保存到CSV文件
* @details 将内存中的所有学生数据以CSV格式保存到STUDENTS_FILE文件中
*/
void saveStudentsToFile();
#endif // STUDENT_IO_H
+176
View File
@@ -0,0 +1,176 @@
/**
* @file student_search.c
* @brief 学生数据搜索和显示功能实现
* @note 负责学生信息的查找、显示等功能
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "globals.h"
#include "io_utils.h"
// 函数前向声明
void displayStudentInfo(const Student *student);
/**
* @brief 按学号查找学生
* @details 根据用户输入的学号精确查找学生信息
* 找到后显示该学生的详细信息
* @note 查找方式:精确匹配学号
* @warning 如果没有学生数据或未找到匹配学生,将显示相应提示信息
*/
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 按姓名查找学生
* @details 根据用户输入的姓名进行模糊查找学生信息
* 支持部分姓名匹配,显示所有匹配的学生详细信息
* @note 查找方式:模糊匹配(包含子字符串)
* @note 如果找到多个匹配学生,将全部显示
* @warning 如果没有学生数据或未找到匹配学生,将显示相应提示信息
*/
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 显示所有学生信息
* @details 以表格形式显示系统中所有学生的基本信息
* 包括学号、姓名、年龄、性别、总分和平均分
* @note 显示格式:表格形式,便于查看和比较
* @note 显示内容:学号、姓名、年龄、性别、总分、平均分
* @note 同时显示总学生数统计
* @warning 如果没有学生数据,将显示警告信息
*/
void displayAllStudents()
{
clearScreen();
printHeader("所有学生信息");
if (studentCount == 0)
{
printWarning("暂无学生数据!");
pauseSystem();
return;
}
printf("\n");
// 调整中文表头的对齐格式,考虑中文字符的显示宽度
printf("%-12s %-10s %-6s %-6s %-10s %-8s\n",
"学号", "姓名", "年龄", "性别", "总分", "平均分");
printf("==========================================\n");
for (int i = 0; i < studentCount; i++)
{
printf("%-10s %-10s %-4d %-4c %-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 显示单个学生详细信息
* @details 显示指定学生的完整详细信息,包括基本信息和所有课程成绩
* @param student 指向要显示信息的学生结构体的常量指针
* @note 显示内容:
* - 基本信息:学号、姓名、年龄、性别、课程数量
* - 课程成绩:每门课程的名称和分数
* - 统计信息:总分和平均分
* @warning 传入的student指针不能为NULL
*/
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);
}
}
+36
View File
@@ -0,0 +1,36 @@
/**
* @file student_search.h
* @brief 学生数据搜索和显示操作头文件
* @note 声明学生数据的查找、显示等功能
*/
#ifndef STUDENT_SEARCH_H
#define STUDENT_SEARCH_H
#include "config.h"
/**
* @brief 按学号查找学生
* @details 根据用户输入的学号精确查找学生信息
*/
void searchStudentByID();
/**
* @brief 按姓名查找学生
* @details 根据用户输入的姓名进行模糊查找学生信息
*/
void searchStudentByName();
/**
* @brief 显示所有学生信息
* @details 以表格形式显示系统中所有学生的基本信息
*/
void displayAllStudents();
/**
* @brief 显示单个学生详细信息
* @param student 指向要显示信息的学生结构体的常量指针
*/
void displayStudentInfo(const Student *student);
#endif // STUDENT_SEARCH_H
+65
View File
@@ -0,0 +1,65 @@
/**
* @file student_sort.c
* @brief 学生数据排序功能实现
* @note 负责学生信息的排序操作
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "globals.h"
/**
* @brief 排序学生信息
* @details 根据指定的排序依据和顺序对学生数组进行排序
* 使用冒泡排序算法实现
* @param criteria 排序依据(SORT_BY_ID, SORT_BY_NAME, SORT_BY_TOTAL_SCORE, SORT_BY_AVERAGE_SCORE
* @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)
{
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;
}
+20
View File
@@ -0,0 +1,20 @@
/**
* @file student_sort.h
* @brief 学生数据排序操作头文件
* @note 声明学生数据的排序功能
*/
#ifndef STUDENT_SORT_H
#define STUDENT_SORT_H
#include "config.h"
/**
* @brief 排序学生信息
* @details 根据指定的排序依据和顺序对学生数组进行排序
* @param criteria 排序依据(SORT_BY_ID, SORT_BY_NAME, SORT_BY_TOTAL_SCORE, SORT_BY_AVERAGE_SCORE
* @param order 排序顺序(SORT_ASCENDING升序, SORT_DESCENDING降序)
*/
void sortStudents(int criteria, int order);
#endif // STUDENT_SORT_H
+68
View File
@@ -0,0 +1,68 @@
/**
* @file system_utils.c
* @brief
* @note
*/
#include <stdio.h>
#include <stdlib.h>
#include "system_utils.h"
#include "file_utils.h"
#include "io_utils.h"
#include "config.h"
/**
* @brief
* @details
* createDataDirectories函数创建数据存储目录
* @return truefalse
* @note
* @note
*/
bool initializeSystem()
{
printInfo("正在初始化系统...");
if (!createDataDirectories())
{
printError("系统初始化失败:无法创建数据目录");
return false;
}
printSuccess("系统初始化完成");
return true;
}
/**
* @brief
* @details
* "data"
* @return truefalse
* @note true
* @note
*/
bool createDataDirectories()
{
// 创建data目录
if (!createDirectory("data"))
{
printError("无法创建data目录");
return false;
}
return true;
}
/**
* @brief
* @details 退
*
* @note 退
* @note
*/
void cleanupSystem()
{
printInfo("正在清理系统资源...");
// 这里可以添加其他清理操作
printSuccess("系统清理完成");
}
+17
View File
@@ -0,0 +1,17 @@
/**
* @file system_utils.h
* @brief
* @note
*/
#ifndef SYSTEM_UTILS_H
#define SYSTEM_UTILS_H
#include <stdbool.h>
// 系统初始化和清理函数
bool initializeSystem(); // 初始化系统
bool createDataDirectories(); // 创建数据目录
void cleanupSystem(); // 清理系统资源
#endif // SYSTEM_UTILS_H
+167 -78
View File
@@ -10,51 +10,74 @@
#include "user_manage.h" #include "user_manage.h"
#include "config.h" #include "config.h"
#include "globals.h" #include "globals.h"
#include "auxiliary.h" #include "io_utils.h"
#include "string_utils.h"
/** /**
* @brief * @brief
* @return 10 * @details
*
* @return int 10
* @note
* 1.
* 2.
* 3. currentUser和isCurrentUserAdmin
* @note
* - currentUser:
* - isCurrentUserAdmin:
*/ */
int loginSystem() { int loginSystem()
{
char username[MAX_USERNAME_LENGTH]; char username[MAX_USERNAME_LENGTH];
char password[MAX_PASSWORD_LENGTH]; char password[MAX_PASSWORD_LENGTH];
printf("\n"); printf("\n");
safeInputString("请输入用户名", username, MAX_USERNAME_LENGTH); safeInputString("请输入用户名", username, MAX_USERNAME_LENGTH);
safeInputString("请输入密码", password, MAX_PASSWORD_LENGTH); safeInputString("请输入密码", password, MAX_PASSWORD_LENGTH);
// 验证用户名和密码 // 验证用户名和密码
for (int i = 0; i < userCount; i++) { for (int i = 0; i < userCount; i++)
if (strcmp(users[i].username, username) == 0 && {
strcmp(users[i].password, password) == 0) { if (strcmp(users[i].username, username) == 0 &&
strcmp(users[i].password, password) == 0)
{
// 登录成功 // 登录成功
strcpy(currentUser, username); strcpy(currentUser, username);
isCurrentUserAdmin = users[i].isAdmin; isCurrentUserAdmin = users[i].isAdmin;
return 1; return 1;
} }
} }
return 0; // 登录失败 return 0; // 登录失败
} }
/** /**
* @brief * @brief
* @details USERS_FILE文件中读取用户数据到内存
*
* @note "username:password:isAdmin"
* isAdmin为1表示管理员0
* @note
* - admin123456
* - teacherpassword
* @warning
*/ */
void loadUsersFromFile() { void loadUsersFromFile()
FILE* file = fopen(USERS_FILE, "r"); {
if (file == NULL) { FILE *file = fopen(USERS_FILE, "r");
if (file == NULL)
{
// 文件不存在,创建默认管理员账户 // 文件不存在,创建默认管理员账户
strcpy(users[0].username, "admin"); strcpy(users[0].username, "admin");
strcpy(users[0].password, "123456"); strcpy(users[0].password, "123456");
users[0].isAdmin = true; users[0].isAdmin = true;
strcpy(users[1].username, "teacher"); strcpy(users[1].username, "teacher");
strcpy(users[1].password, "password"); strcpy(users[1].password, "password");
users[1].isAdmin = false; users[1].isAdmin = false;
userCount = 2; userCount = 2;
// 保存默认用户到文件 // 保存默认用户到文件
saveUsersToFile(); saveUsersToFile();
printInfo("已创建默认用户账户:"); printInfo("已创建默认用户账户:");
@@ -62,204 +85,270 @@ void loadUsersFromFile() {
printInfo("普通用户 - 用户名: teacher, 密码: password"); printInfo("普通用户 - 用户名: teacher, 密码: password");
return; return;
} }
userCount = 0; userCount = 0;
char line[200]; char line[200];
while (fgets(line, sizeof(line), file) && userCount < MAX_USERS) { while (fgets(line, sizeof(line), file) && userCount < MAX_USERS)
{
// 移除换行符 // 移除换行符
line[strcspn(line, "\n")] = '\0'; line[strcspn(line, "\n")] = '\0';
// 解析格式:username:password:isAdmin // 解析格式:username:password:isAdmin
char* username = strtok(line, ":"); char *username = strtok(line, ":");
char* password = strtok(NULL, ":"); char *password = strtok(NULL, ":");
char* adminFlag = strtok(NULL, ":"); char *adminFlag = strtok(NULL, ":");
if (username && password && adminFlag) { if (username && password && adminFlag)
{
strcpy(users[userCount].username, username); strcpy(users[userCount].username, username);
strcpy(users[userCount].password, password); strcpy(users[userCount].password, password);
users[userCount].isAdmin = (strcmp(adminFlag, "1") == 0); users[userCount].isAdmin = (strcmp(adminFlag, "1") == 0);
userCount++; userCount++;
} }
} }
fclose(file); fclose(file);
} }
/** /**
* @brief * @brief
* @details USERS_FILE文件中
*
* @note "username:password:isAdmin"
* isAdmin为1表示管理员0
* @warning
*/ */
void saveUsersToFile() { void saveUsersToFile()
FILE* file = fopen(USERS_FILE, "w"); {
if (file == NULL) { FILE *file = fopen(USERS_FILE, "w");
if (file == NULL)
{
printError("无法保存用户数据!"); printError("无法保存用户数据!");
return; return;
} }
for (int i = 0; i < userCount; i++) { for (int i = 0; i < userCount; i++)
fprintf(file, "%s:%s:%d\n", {
users[i].username, fprintf(file, "%s:%s:%d\n",
users[i].password, users[i].username,
users[i].password,
users[i].isAdmin ? 1 : 0); users[i].isAdmin ? 1 : 0);
} }
fclose(file); fclose(file);
} }
/** /**
* @brief * @brief
* @details
*
* @note
* 1.
* 2.
* 3.
* 4. /
* 5.
* @warning MAX_USERS上限
* @warning
*/ */
void addUserAccount() { void addUserAccount()
{
clearScreen(); clearScreen();
printHeader("添加用户账户"); printHeader("添加用户账户");
if (userCount >= MAX_USERS) { if (userCount >= MAX_USERS)
{
printError("用户数量已达上限!"); printError("用户数量已达上限!");
pauseSystem(); pauseSystem();
return; return;
} }
char username[MAX_USERNAME_LENGTH]; char username[MAX_USERNAME_LENGTH];
char password[MAX_PASSWORD_LENGTH]; char password[MAX_PASSWORD_LENGTH];
printf("\n"); printf("\n");
safeInputString("请输入新用户名", username, MAX_USERNAME_LENGTH); safeInputString("请输入新用户名", username, MAX_USERNAME_LENGTH);
// 检查用户名是否已存在 // 检查用户名是否已存在
for (int i = 0; i < userCount; i++) { for (int i = 0; i < userCount; i++)
if (strcmp(users[i].username, username) == 0) { {
if (strcmp(users[i].username, username) == 0)
{
printError("用户名已存在!"); printError("用户名已存在!");
pauseSystem(); pauseSystem();
return; return;
} }
} }
safeInputString("请输入密码", password, MAX_PASSWORD_LENGTH); safeInputString("请输入密码", password, MAX_PASSWORD_LENGTH);
printf("\n用户类型:\n"); printf("\n用户类型:\n");
printf("1. 普通用户\n"); printf("1. 普通用户\n");
printf("2. 管理员\n"); printf("2. 管理员\n");
int userType = safeInputInt("请选择用户类型", 1, 2); int userType = safeInputInt("请选择用户类型", 1, 2);
// 添加新用户 // 添加新用户
strcpy(users[userCount].username, username); strcpy(users[userCount].username, username);
strcpy(users[userCount].password, password); strcpy(users[userCount].password, password);
users[userCount].isAdmin = (userType == 2); users[userCount].isAdmin = (userType == 2);
userCount++; userCount++;
// 保存到文件 // 保存到文件
saveUsersToFile(); saveUsersToFile();
dataModified = true; dataModified = true;
printSuccess("用户添加成功!"); printSuccess("用户添加成功!");
pauseSystem(); pauseSystem();
} }
/** /**
* @brief * @brief
* @details
*
* @note
* -
* -
* @note
* 1.
* 2.
* 3.
* 4.
* 5.
* @warning
*/ */
void deleteUserAccount() { void deleteUserAccount()
{
clearScreen(); clearScreen();
printHeader("删除用户账户"); printHeader("删除用户账户");
if (userCount <= 1) { if (userCount <= 1)
{
printError("至少需要保留一个用户账户!"); printError("至少需要保留一个用户账户!");
pauseSystem(); pauseSystem();
return; return;
} }
char username[MAX_USERNAME_LENGTH]; char username[MAX_USERNAME_LENGTH];
printf("\n"); printf("\n");
safeInputString("请输入要删除的用户名", username, MAX_USERNAME_LENGTH); safeInputString("请输入要删除的用户名", username, MAX_USERNAME_LENGTH);
// 不能删除当前登录用户 // 不能删除当前登录用户
if (strcmp(username, currentUser) == 0) { if (strcmp(username, currentUser) == 0)
{
printError("不能删除当前登录的用户!"); printError("不能删除当前登录的用户!");
pauseSystem(); pauseSystem();
return; return;
} }
// 查找并删除用户 // 查找并删除用户
for (int i = 0; i < userCount; i++) { for (int i = 0; i < userCount; i++)
if (strcmp(users[i].username, username) == 0) { {
if (strcmp(users[i].username, username) == 0)
{
// 移动后面的用户向前 // 移动后面的用户向前
for (int j = i; j < userCount - 1; j++) { for (int j = i; j < userCount - 1; j++)
{
users[j] = users[j + 1]; users[j] = users[j + 1];
} }
userCount--; userCount--;
// 保存到文件 // 保存到文件
saveUsersToFile(); saveUsersToFile();
dataModified = true; dataModified = true;
printSuccess("用户删除成功!"); printSuccess("用户删除成功!");
pauseSystem(); pauseSystem();
return; return;
} }
} }
printError("用户不存在!"); printError("用户不存在!");
pauseSystem(); pauseSystem();
} }
/** /**
* @brief * @brief
* @details
*
* @note
* 1.
* 2.
* 3.
* 4.
* 5.
* @warning 使
*/ */
void modifyUserPassword() { void modifyUserPassword()
{
clearScreen(); clearScreen();
printHeader("修改用户密码"); printHeader("修改用户密码");
char username[MAX_USERNAME_LENGTH]; char username[MAX_USERNAME_LENGTH];
char newPassword[MAX_PASSWORD_LENGTH]; char newPassword[MAX_PASSWORD_LENGTH];
printf("\n"); printf("\n");
safeInputString("请输入用户名", username, MAX_USERNAME_LENGTH); safeInputString("请输入用户名", username, MAX_USERNAME_LENGTH);
// 查找用户 // 查找用户
for (int i = 0; i < userCount; i++) { for (int i = 0; i < userCount; i++)
if (strcmp(users[i].username, username) == 0) { {
if (strcmp(users[i].username, username) == 0)
{
safeInputString("请输入新密码", newPassword, MAX_PASSWORD_LENGTH); safeInputString("请输入新密码", newPassword, MAX_PASSWORD_LENGTH);
strcpy(users[i].password, newPassword); strcpy(users[i].password, newPassword);
// 保存到文件 // 保存到文件
saveUsersToFile(); saveUsersToFile();
dataModified = true; dataModified = true;
printSuccess("密码修改成功!"); printSuccess("密码修改成功!");
pauseSystem(); pauseSystem();
return; return;
} }
} }
printError("用户不存在!"); printError("用户不存在!");
pauseSystem(); pauseSystem();
} }
/** /**
* @brief * @brief
* @details
*
* @note
* -
* - /
* -
* -
* @note 便
* @warning
*/ */
void viewAllUsers() { void viewAllUsers()
{
clearScreen(); clearScreen();
printHeader("所有用户列表"); printHeader("所有用户列表");
if (userCount == 0) { if (userCount == 0)
{
printWarning("暂无用户数据!"); printWarning("暂无用户数据!");
pauseSystem(); pauseSystem();
return; return;
} }
printf("\n"); printf("\n");
printf("%-20s %-15s %-10s\n", "用户名", "用户类型", "状态"); printf("%-20s %-15s %-10s\n", "用户名", "用户类型", "状态");
printSeparator(); printSeparator();
for (int i = 0; i < userCount; i++) { for (int i = 0; i < userCount; i++)
printf("%-20s %-15s %-10s\n", {
printf("%-20s %-15s %-10s\n",
users[i].username, users[i].username,
users[i].isAdmin ? "管理员" : "普通用户", users[i].isAdmin ? "管理员" : "普通用户",
strcmp(users[i].username, currentUser) == 0 ? "当前用户" : ""); strcmp(users[i].username, currentUser) == 0 ? "当前用户" : "");
} }
printf("\n总用户数: %d\n", userCount); printf("\n总用户数: %d\n", userCount);
pauseSystem(); pauseSystem();
} }
+2
View File
@@ -1,6 +1,8 @@
#ifndef USER_MANAGE_H #ifndef USER_MANAGE_H
#define USER_MANAGE_H #define USER_MANAGE_H
#include "config.h"
// 用户认证与管理相关函数 // 用户认证与管理相关函数
int loginSystem(); // 处理用户登录 int loginSystem(); // 处理用户登录
void loadUsersFromFile(); // 从文件加载用户数据 void loadUsersFromFile(); // 从文件加载用户数据
+86
View File
@@ -0,0 +1,86 @@
/**
* @file validation.c
* @brief
* @note
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "validation.h"
#include "config.h"
#include "string_utils.h"
/**
* @brief
* @details 0-100
* @param score
* @return truefalse
* @note 0.0100.0
*/
bool isValidScore(float score)
{
return score >= 0.0 && score <= 100.0;
}
/**
* @brief
* @details
* @param id
* @return truefalse
* @note 1MAX_ID_LENGTH之间
* @warning id为NULLfalse
*/
bool isValidStudentId(const char *id)
{
if (id == NULL || isEmptyString(id))
{
return false;
}
int len = strlen(id);
return len > 0 && len <= MAX_ID_LENGTH;
}
/**
* @brief
* @details
* @param name
* @return truefalse
* @note 1MAX_NAME_LENGTH之间
* @warning name为NULLfalse
*/
bool isValidName(const char *name)
{
if (name == NULL || isEmptyString(name))
{
return false;
}
int len = strlen(name);
return len > 0 && len <= MAX_NAME_LENGTH;
}
/**
* @brief
* @details 'M''F'
* @param gender
* @return truefalse
* @note 'M''F'
*/
bool isValidGender(char gender)
{
return gender == GENDER_MALE || gender == GENDER_FEMALE;
}
/**
* @brief
* @details
* @param age
* @return truefalse
* @note MIN_AGE到MAX_AGE
*/
bool isValidAge(int age)
{
return age >= MIN_AGE && age <= MAX_AGE;
}
+19
View File
@@ -0,0 +1,19 @@
/**
* @file validation.h
* @brief
* @note
*/
#ifndef VALIDATION_H
#define VALIDATION_H
#include <stdbool.h>
// 数据验证函数
bool isValidScore(float score); // 验证成绩是否有效
bool isValidStudentId(const char* id); // 验证学号是否有效
bool isValidName(const char* name); // 验证姓名是否有效
bool isValidGender(char gender); // 验证性别是否有效
bool isValidAge(int age); // 验证年龄是否有效
#endif // VALIDATION_H