main
This commit is contained in:
-449
@@ -1,449 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file auxiliary.c
|
|
||||||
* @brief 辅助函数实现文件
|
|
||||||
* @note 实现系统中使用的各种辅助函数
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <direct.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#else
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "auxiliary.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include "stu.data.h"
|
|
||||||
#include "user_manage.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清理输入缓冲区
|
|
||||||
*/
|
|
||||||
void clearInputBuffer()
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
while ((c = getchar()) != '\n' && c != EOF)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 暂停系统,等待用户按键
|
|
||||||
*/
|
|
||||||
void pauseSystem()
|
|
||||||
{
|
|
||||||
printf("\n按任意键继续...");
|
|
||||||
#ifdef _WIN32
|
|
||||||
_getch();
|
|
||||||
#else
|
|
||||||
getchar();
|
|
||||||
#endif
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清屏
|
|
||||||
*/
|
|
||||||
void clearScreen()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
system("cls");
|
|
||||||
#else
|
|
||||||
system("clear");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 打印分隔线
|
|
||||||
*/
|
|
||||||
void printSeparator()
|
|
||||||
{
|
|
||||||
printf("========================================\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 打印标题头
|
|
||||||
* @param title 标题文本
|
|
||||||
*/
|
|
||||||
void printHeader(const char *title)
|
|
||||||
{
|
|
||||||
printSeparator();
|
|
||||||
printf(" %s\n", title);
|
|
||||||
printSeparator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 验证分数是否合法
|
|
||||||
* @param score 分数
|
|
||||||
* @return 合法返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isValidScore(float score)
|
|
||||||
{
|
|
||||||
return score >= MIN_SCORE && score <= MAX_SCORE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 验证学号格式
|
|
||||||
* @param id 学号
|
|
||||||
* @return 合法返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isValidStudentID(const char *id)
|
|
||||||
{
|
|
||||||
if (id == NULL || strlen(id) == 0 || strlen(id) >= MAX_ID_LENGTH)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否只包含数字和字母
|
|
||||||
for (int i = 0; id[i] != '\0'; i++)
|
|
||||||
{
|
|
||||||
if (!isalnum(id[i]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 验证姓名格式
|
|
||||||
* @param name 姓名
|
|
||||||
* @return 合法返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isValidName(const char *name)
|
|
||||||
{
|
|
||||||
if (name == NULL || strlen(name) == 0 || strlen(name) >= MAX_NAME_LENGTH)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否包含非法字符
|
|
||||||
for (int i = 0; name[i] != '\0'; i++)
|
|
||||||
{
|
|
||||||
if (!isalpha(name[i]) && name[i] != ' ' && name[i] != '-' &&
|
|
||||||
(unsigned char)name[i] < 128)
|
|
||||||
{ // 允许中文字符
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 验证性别
|
|
||||||
* @param gender 性别字符
|
|
||||||
* @return 合法返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isValidGender(char gender)
|
|
||||||
{
|
|
||||||
return gender == GENDER_MALE || gender == GENDER_FEMALE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 验证年龄
|
|
||||||
* @param age 年龄
|
|
||||||
* @return 合法返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isValidAge(int age)
|
|
||||||
{
|
|
||||||
return age >= 10 && age <= 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 去除字符串首尾空格
|
|
||||||
* @param str 字符串
|
|
||||||
*/
|
|
||||||
void trimString(char *str)
|
|
||||||
{
|
|
||||||
if (str == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// 去除开头空格
|
|
||||||
char *start = str;
|
|
||||||
while (isspace(*start))
|
|
||||||
start++;
|
|
||||||
|
|
||||||
// 去除结尾空格
|
|
||||||
char *end = str + strlen(str) - 1;
|
|
||||||
while (end > start && isspace(*end))
|
|
||||||
end--;
|
|
||||||
|
|
||||||
// 移动字符串
|
|
||||||
int len = end - start + 1;
|
|
||||||
memmove(str, start, len);
|
|
||||||
str[len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 判断字符串是否为空
|
|
||||||
* @param str 字符串
|
|
||||||
* @return 为空返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool isEmptyString(const char *str)
|
|
||||||
{
|
|
||||||
if (str == NULL)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
while (*str)
|
|
||||||
{
|
|
||||||
if (!isspace(*str))
|
|
||||||
return false;
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查文件是否存在
|
|
||||||
* @param filename 文件名
|
|
||||||
* @return 存在返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool fileExists(const char *filename)
|
|
||||||
{
|
|
||||||
FILE *file = fopen(filename, "r");
|
|
||||||
if (file)
|
|
||||||
{
|
|
||||||
fclose(file);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建目录
|
|
||||||
* @param path 目录路径
|
|
||||||
* @return 成功返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool createDirectory(const char *path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return _mkdir(path) == 0 || errno == EEXIST;
|
|
||||||
#else
|
|
||||||
return mkdir(path, 0755) == 0 || errno == EEXIST;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 计算平均分
|
|
||||||
* @param scores 分数数组
|
|
||||||
* @param count 分数个数
|
|
||||||
* @return 平均分
|
|
||||||
*/
|
|
||||||
float calculateAverage(float scores[], int count)
|
|
||||||
{
|
|
||||||
if (count <= 0)
|
|
||||||
return 0.0;
|
|
||||||
|
|
||||||
float sum = 0.0;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
sum += scores[i];
|
|
||||||
}
|
|
||||||
return sum / count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 安全输入整数
|
|
||||||
* @param prompt 提示信息
|
|
||||||
* @param min 最小值
|
|
||||||
* @param max 最大值
|
|
||||||
* @return 输入的整数
|
|
||||||
*/
|
|
||||||
int safeInputInt(const char *prompt, int min, int max)
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
char buffer[100];
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
printf("%s (%d-%d): ", prompt, min, max);
|
|
||||||
|
|
||||||
if (fgets(buffer, sizeof(buffer), stdin) != NULL)
|
|
||||||
{
|
|
||||||
if (sscanf(buffer, "%d", &value) == 1)
|
|
||||||
{
|
|
||||||
if (value >= min && value <= max)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printError("输入无效,请重新输入!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 安全输入浮点数
|
|
||||||
* @param prompt 提示信息
|
|
||||||
* @param min 最小值
|
|
||||||
* @param max 最大值
|
|
||||||
* @return 输入的浮点数
|
|
||||||
*/
|
|
||||||
float safeInputFloat(const char *prompt, float min, float max)
|
|
||||||
{
|
|
||||||
float value;
|
|
||||||
char buffer[100];
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
printf("%s (%.1f-%.1f): ", prompt, min, max);
|
|
||||||
|
|
||||||
if (fgets(buffer, sizeof(buffer), stdin) != NULL)
|
|
||||||
{
|
|
||||||
if (sscanf(buffer, "%f", &value) == 1)
|
|
||||||
{
|
|
||||||
if (value >= min && value <= max)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printError("输入无效,请重新输入!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 安全输入字符串
|
|
||||||
* @param prompt 提示信息
|
|
||||||
* @param buffer 缓冲区
|
|
||||||
* @param maxLen 最大长度
|
|
||||||
*/
|
|
||||||
void safeInputString(const char *prompt, char *buffer, int maxLen)
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
printf("%s: ", prompt);
|
|
||||||
|
|
||||||
if (fgets(buffer, maxLen, stdin) != NULL)
|
|
||||||
{
|
|
||||||
// 移除换行符
|
|
||||||
buffer[strcspn(buffer, "\n")] = '\0';
|
|
||||||
trimString(buffer);
|
|
||||||
|
|
||||||
if (!isEmptyString(buffer))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printError("输入不能为空,请重新输入!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 彩色输出
|
|
||||||
* @param text 文本
|
|
||||||
* @param color 颜色代码
|
|
||||||
*/
|
|
||||||
void printColored(const char *text, const char *color)
|
|
||||||
{
|
|
||||||
printf("%s%s%s", color, text, COLOR_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 成功消息
|
|
||||||
* @param message 消息
|
|
||||||
*/
|
|
||||||
void printSuccess(const char *message)
|
|
||||||
{
|
|
||||||
printColored(message, COLOR_GREEN);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 错误消息
|
|
||||||
* @param message 消息
|
|
||||||
*/
|
|
||||||
void printError(const char *message)
|
|
||||||
{
|
|
||||||
printColored(message, COLOR_RED);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 警告消息
|
|
||||||
* @param message 消息
|
|
||||||
*/
|
|
||||||
void printWarning(const char *message)
|
|
||||||
{
|
|
||||||
printColored(message, COLOR_YELLOW);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 信息消息
|
|
||||||
* @param message 消息
|
|
||||||
*/
|
|
||||||
void printInfo(const char *message)
|
|
||||||
{
|
|
||||||
printColored(message, COLOR_CYAN);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化系统
|
|
||||||
* @return 成功返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool initializeSystem()
|
|
||||||
{
|
|
||||||
// 创建数据目录
|
|
||||||
if (!createDataDirectories())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载用户数据
|
|
||||||
loadUsersFromFile();
|
|
||||||
|
|
||||||
// 加载学生数据
|
|
||||||
loadStudentsFromFile();
|
|
||||||
|
|
||||||
systemInitialized = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建数据目录
|
|
||||||
* @return 成功返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool createDataDirectories()
|
|
||||||
{
|
|
||||||
if (!createDirectory("data"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!createDirectory(BACKUP_DIR))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清理系统资源
|
|
||||||
*/
|
|
||||||
void cleanupSystem()
|
|
||||||
{
|
|
||||||
// 保存数据
|
|
||||||
if (dataModified)
|
|
||||||
{
|
|
||||||
saveStudentsToFile();
|
|
||||||
saveUsersToFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
systemInitialized = false;
|
|
||||||
}
|
|
||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file auxiliary.h
|
|
||||||
* @brief 辅助函数头文件
|
|
||||||
* @note 包含系统中使用的各种辅助函数声明
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXILIARY_H
|
|
||||||
#define AUXILIARY_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "main_menu.h"
|
|
||||||
|
|
||||||
// 输入输出辅助函数
|
|
||||||
void clearInputBuffer(); // 清理输入缓冲区
|
|
||||||
void pauseSystem(); // 暂停系统,等待用户按键
|
|
||||||
void clearScreen(); // 清屏
|
|
||||||
void printSeparator(); // 打印分隔线
|
|
||||||
void printHeader(const char* title); // 打印标题头
|
|
||||||
|
|
||||||
// 数据验证函数
|
|
||||||
bool isValidScore(float score); // 验证分数是否合法 (0-100)
|
|
||||||
bool isValidStudentID(const char* id); // 验证学号格式
|
|
||||||
bool isValidName(const char* name); // 验证姓名格式
|
|
||||||
bool isValidGender(char gender); // 验证性别
|
|
||||||
bool isValidAge(int age); // 验证年龄
|
|
||||||
|
|
||||||
// 字符串处理函数
|
|
||||||
void trimString(char* str); // 去除字符串首尾空格
|
|
||||||
bool isEmptyString(const char* str); // 判断字符串是否为空
|
|
||||||
void toLowerCase(char* str); // 转换为小写
|
|
||||||
void toUpperCase(char* str); // 转换为大写
|
|
||||||
|
|
||||||
// 文件操作辅助函数
|
|
||||||
bool fileExists(const char* filename); // 检查文件是否存在
|
|
||||||
bool createDirectory(const char* path); // 创建目录
|
|
||||||
bool backupFile(const char* source, const char* backup); // 备份文件
|
|
||||||
|
|
||||||
// 数学计算辅助函数
|
|
||||||
float calculateAverage(float scores[], int count); // 计算平均分
|
|
||||||
float findMaxScore(float scores[], int count); // 找最高分
|
|
||||||
float findMinScore(float scores[], int count); // 找最低分
|
|
||||||
|
|
||||||
|
|
||||||
// 安全输入函数
|
|
||||||
int safeInputInt(const char* prompt, int min, int max); // 安全输入整数
|
|
||||||
float safeInputFloat(const char* prompt, float min, float max); // 安全输入浮点数
|
|
||||||
void safeInputString(const char* prompt, char* buffer, int maxLen); // 安全输入字符串
|
|
||||||
char safeInputChar(const char* prompt, const char* validChars); // 安全输入字符
|
|
||||||
|
|
||||||
// 系统初始化和清理函数
|
|
||||||
bool initializeSystem(); // 初始化系统
|
|
||||||
void cleanupSystem(); // 清理系统资源
|
|
||||||
bool createDataDirectories(); // 创建数据目录
|
|
||||||
|
|
||||||
// 颜色输出函数
|
|
||||||
void printColored(const char* text, const char* color); // 彩色输出
|
|
||||||
void printSuccess(const char* message); // 成功消息
|
|
||||||
void printError(const char* message); // 错误消息
|
|
||||||
void printWarning(const char* message); // 警告消息
|
|
||||||
void printInfo(const char* message); // 信息消息
|
|
||||||
|
|
||||||
#endif // AUXILIARY_H
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
#ifndef STU_DATA_H
|
|
||||||
#define STU_DATA_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// 学生数据管理相关函数 (CRUD)
|
|
||||||
void loadStudentsFromFile(); // 从文件加载学生数据
|
|
||||||
void saveStudentsToFile(); // 将学生数据保存到文件
|
|
||||||
void addStudent(); // 增加学生信息
|
|
||||||
void deleteStudent(); // 删除学生信息
|
|
||||||
void modifyStudent(); // 修改学生信息
|
|
||||||
void searchStudentByID(); // 按学号查找学生
|
|
||||||
void searchStudentByName(); // 按姓名查找学生
|
|
||||||
void displayAllStudents(); // 显示所有学生信息
|
|
||||||
void displayStudentInfo(const Student* student); // 显示单个学生信息
|
|
||||||
void sortStudents(int criteria, int order); // 统一排序函数,根据参数选择排序依据和升降序
|
|
||||||
|
|
||||||
#endif // STU_DATA_H
|
|
||||||
-614
@@ -1,614 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file stu_data.c
|
|
||||||
* @brief 学生数据管理实现文件
|
|
||||||
* @note 实现学生信息的增删改查功能
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "stu.data.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include "auxiliary.h"
|
|
||||||
#include "statistical_analysis.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从CSV文件加载学生数据
|
|
||||||
*/
|
|
||||||
void loadStudentsFromFile() {
|
|
||||||
FILE* file = fopen(STUDENTS_FILE, "r");
|
|
||||||
if (file == NULL) {
|
|
||||||
studentCount = 0;
|
|
||||||
printInfo("学生数据文件不存在,将创建新文件。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char line[1024];
|
|
||||||
studentCount = 0;
|
|
||||||
|
|
||||||
// 跳过CSV头部
|
|
||||||
if (fgets(line, sizeof(line), file) == NULL) {
|
|
||||||
fclose(file);
|
|
||||||
studentCount = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取学生数据
|
|
||||||
while (fgets(line, sizeof(line), file) != NULL && studentCount < MAX_STUDENTS) {
|
|
||||||
Student* student = &students[studentCount];
|
|
||||||
memset(student, 0, sizeof(Student));
|
|
||||||
|
|
||||||
// 解析CSV行
|
|
||||||
char* token = strtok(line, ",");
|
|
||||||
if (token == NULL) continue;
|
|
||||||
|
|
||||||
// 学号
|
|
||||||
strncpy(student->studentID, token, MAX_ID_LENGTH - 1);
|
|
||||||
|
|
||||||
// 姓名
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token == NULL) continue;
|
|
||||||
strncpy(student->name, token, MAX_NAME_LENGTH - 1);
|
|
||||||
|
|
||||||
// 年龄
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token == NULL) continue;
|
|
||||||
student->age = atoi(token);
|
|
||||||
|
|
||||||
// 性别
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token == NULL) continue;
|
|
||||||
student->gender = token[0];
|
|
||||||
|
|
||||||
// 课程数量
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token == NULL) continue;
|
|
||||||
student->courseCount = atoi(token);
|
|
||||||
|
|
||||||
// 课程和成绩
|
|
||||||
for (int i = 0; i < student->courseCount && i < MAX_COURSES; i++) {
|
|
||||||
// 课程名称
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token == NULL) break;
|
|
||||||
strncpy(student->courses[i], token, MAX_COURSE_NAME_LENGTH - 1);
|
|
||||||
|
|
||||||
// 成绩
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token == NULL) break;
|
|
||||||
student->scores[i] = atof(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 总分
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token != NULL) {
|
|
||||||
student->totalScore = atof(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 平均分
|
|
||||||
token = strtok(NULL, ",");
|
|
||||||
if (token != NULL) {
|
|
||||||
student->averageScore = atof(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
studentCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
// 更新统计信息
|
|
||||||
statsNeedUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 将学生数据保存到CSV文件
|
|
||||||
*/
|
|
||||||
void saveStudentsToFile() {
|
|
||||||
FILE* file = fopen(STUDENTS_FILE, "w");
|
|
||||||
if (file == NULL) {
|
|
||||||
printError("无法保存学生数据!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入CSV头部
|
|
||||||
fprintf(file, "学号,姓名,年龄,性别,课程数量");
|
|
||||||
for (int i = 0; i < MAX_COURSES; i++) {
|
|
||||||
fprintf(file, ",课程%d,成绩%d", i+1, i+1);
|
|
||||||
}
|
|
||||||
fprintf(file, ",总分,平均分\n");
|
|
||||||
|
|
||||||
// 写入学生数据
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
Student* student = &students[i];
|
|
||||||
|
|
||||||
// 基本信息
|
|
||||||
fprintf(file, "%s,%s,%d,%c,%d",
|
|
||||||
student->studentID,
|
|
||||||
student->name,
|
|
||||||
student->age,
|
|
||||||
student->gender,
|
|
||||||
student->courseCount);
|
|
||||||
|
|
||||||
// 课程和成绩
|
|
||||||
for (int j = 0; j < MAX_COURSES; j++) {
|
|
||||||
if (j < student->courseCount) {
|
|
||||||
fprintf(file, ",%s,%.2f", student->courses[j], student->scores[j]);
|
|
||||||
} else {
|
|
||||||
fprintf(file, ",,"); // 空的课程和成绩
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 总分和平均分
|
|
||||||
fprintf(file, ",%.2f,%.2f\n", student->totalScore, student->averageScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
dataModified = false;
|
|
||||||
|
|
||||||
printSuccess("学生数据已保存到CSV文件!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 添加学生信息
|
|
||||||
*/
|
|
||||||
void addStudent() {
|
|
||||||
clearScreen();
|
|
||||||
printHeader("添加学生信息");
|
|
||||||
|
|
||||||
if (studentCount >= MAX_STUDENTS) {
|
|
||||||
printError("学生数量已达上限!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Student newStudent;
|
|
||||||
memset(&newStudent, 0, sizeof(Student));
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// 输入学号
|
|
||||||
while (1) {
|
|
||||||
safeInputString("请输入学号", newStudent.studentID, MAX_ID_LENGTH);
|
|
||||||
|
|
||||||
if (!isValidStudentID(newStudent.studentID)) {
|
|
||||||
printError("学号格式无效!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查学号是否已存在
|
|
||||||
bool exists = false;
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
if (strcmp(students[i].studentID, newStudent.studentID) == 0) {
|
|
||||||
printError("学号已存在!");
|
|
||||||
exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exists) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输入姓名
|
|
||||||
while (1) {
|
|
||||||
safeInputString("请输入姓名", newStudent.name, MAX_NAME_LENGTH);
|
|
||||||
if (isValidName(newStudent.name)) break;
|
|
||||||
printError("姓名格式无效!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输入年龄
|
|
||||||
newStudent.age = safeInputInt("请输入年龄", 10, 100);
|
|
||||||
|
|
||||||
// 输入性别
|
|
||||||
while (1) {
|
|
||||||
printf("请输入性别 (M/F): ");
|
|
||||||
char gender;
|
|
||||||
scanf(" %c", &gender);
|
|
||||||
clearInputBuffer();
|
|
||||||
|
|
||||||
if (isValidGender(gender)) {
|
|
||||||
newStudent.gender = gender;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printError("性别输入无效!请输入 M 或 F");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输入课程信息
|
|
||||||
printf("\n开始输入课程信息:\n");
|
|
||||||
newStudent.courseCount = 0;
|
|
||||||
|
|
||||||
while (newStudent.courseCount < MAX_COURSES) {
|
|
||||||
printf("\n第 %d 门课程:\n", newStudent.courseCount + 1);
|
|
||||||
|
|
||||||
safeInputString("课程名称",
|
|
||||||
newStudent.courses[newStudent.courseCount],
|
|
||||||
MAX_COURSE_NAME_LENGTH);
|
|
||||||
|
|
||||||
newStudent.scores[newStudent.courseCount] =
|
|
||||||
safeInputFloat("课程分数", MIN_SCORE, MAX_SCORE);
|
|
||||||
|
|
||||||
newStudent.courseCount++;
|
|
||||||
|
|
||||||
if (newStudent.courseCount < MAX_COURSES) {
|
|
||||||
printf("\n是否继续添加课程?(y/n): ");
|
|
||||||
char choice;
|
|
||||||
scanf(" %c", &choice);
|
|
||||||
clearInputBuffer();
|
|
||||||
|
|
||||||
if (choice != 'y' && choice != 'Y') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算总分和平均分
|
|
||||||
calculateStudentStats(&newStudent);
|
|
||||||
|
|
||||||
// 添加到数组
|
|
||||||
students[studentCount] = newStudent;
|
|
||||||
studentCount++;
|
|
||||||
|
|
||||||
// 标记数据已修改
|
|
||||||
dataModified = true;
|
|
||||||
statsNeedUpdate = true;
|
|
||||||
|
|
||||||
printSuccess("学生信息添加成功!");
|
|
||||||
printf("学号: %s\n", newStudent.studentID);
|
|
||||||
printf("姓名: %s\n", newStudent.name);
|
|
||||||
printf("总分: %.2f\n", newStudent.totalScore);
|
|
||||||
printf("平均分: %.2f\n", newStudent.averageScore);
|
|
||||||
|
|
||||||
pauseSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 删除学生信息
|
|
||||||
*/
|
|
||||||
void deleteStudent() {
|
|
||||||
clearScreen();
|
|
||||||
printHeader("删除学生信息");
|
|
||||||
|
|
||||||
if (studentCount == 0) {
|
|
||||||
printWarning("暂无学生数据!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char studentID[MAX_ID_LENGTH];
|
|
||||||
printf("\n");
|
|
||||||
safeInputString("请输入要删除的学生学号", studentID, MAX_ID_LENGTH);
|
|
||||||
|
|
||||||
// 查找学生
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
if (strcmp(students[i].studentID, studentID) == 0) {
|
|
||||||
printf("\n找到学生信息:\n");
|
|
||||||
printf("学号: %s\n", students[i].studentID);
|
|
||||||
printf("姓名: %s\n", students[i].name);
|
|
||||||
|
|
||||||
printf("\n确认删除?(y/n): ");
|
|
||||||
char choice;
|
|
||||||
scanf(" %c", &choice);
|
|
||||||
clearInputBuffer();
|
|
||||||
|
|
||||||
if (choice == 'y' || choice == 'Y') {
|
|
||||||
// 移动后面的学生向前
|
|
||||||
for (int j = i; j < studentCount - 1; j++) {
|
|
||||||
students[j] = students[j + 1];
|
|
||||||
}
|
|
||||||
studentCount--;
|
|
||||||
|
|
||||||
dataModified = true;
|
|
||||||
statsNeedUpdate = true;
|
|
||||||
|
|
||||||
printSuccess("学生信息删除成功!");
|
|
||||||
} else {
|
|
||||||
printInfo("删除操作已取消。");
|
|
||||||
}
|
|
||||||
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printError("未找到该学号的学生!");
|
|
||||||
pauseSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 修改学生信息
|
|
||||||
*/
|
|
||||||
void modifyStudent() {
|
|
||||||
clearScreen();
|
|
||||||
printHeader("修改学生信息");
|
|
||||||
|
|
||||||
if (studentCount == 0) {
|
|
||||||
printWarning("暂无学生数据!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char studentID[MAX_ID_LENGTH];
|
|
||||||
printf("\n");
|
|
||||||
safeInputString("请输入要修改的学生学号", studentID, MAX_ID_LENGTH);
|
|
||||||
|
|
||||||
// 查找学生
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
if (strcmp(students[i].studentID, studentID) == 0) {
|
|
||||||
printf("\n找到学生信息:\n");
|
|
||||||
printf("学号: %s\n", students[i].studentID);
|
|
||||||
printf("姓名: %s\n", students[i].name);
|
|
||||||
printf("年龄: %d\n", students[i].age);
|
|
||||||
printf("性别: %c\n", students[i].gender);
|
|
||||||
|
|
||||||
printf("\n修改选项:\n");
|
|
||||||
printf("1. 修改姓名\n");
|
|
||||||
printf("2. 修改年龄\n");
|
|
||||||
printf("3. 修改性别\n");
|
|
||||||
printf("4. 修改课程成绩\n");
|
|
||||||
printf("0. 返回\n");
|
|
||||||
|
|
||||||
int choice = safeInputInt("请选择修改项", 0, 4);
|
|
||||||
|
|
||||||
switch (choice) {
|
|
||||||
case 1: // 修改姓名
|
|
||||||
safeInputString("请输入新姓名", students[i].name, MAX_NAME_LENGTH);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
students[i].age = safeInputInt("请输入新年龄", 10, 100);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
while (1) {
|
|
||||||
printf("请输入新性别 (M/F): ");
|
|
||||||
char gender;
|
|
||||||
scanf(" %c", &gender);
|
|
||||||
clearInputBuffer();
|
|
||||||
|
|
||||||
if (isValidGender(gender)) {
|
|
||||||
students[i].gender = gender;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printError("性别输入无效!");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
printf("\n当前课程列表:\n");
|
|
||||||
for (int j = 0; j < students[i].courseCount; j++) {
|
|
||||||
printf("%d. %s: %.2f\n", j + 1, students[i].courses[j], students[i].scores[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n修改选项:\n");
|
|
||||||
printf("1. 修改现有课程成绩\n");
|
|
||||||
printf("2. 添加新课程\n");
|
|
||||||
printf("3. 删除课程\n");
|
|
||||||
printf("0. 返回\n");
|
|
||||||
|
|
||||||
int courseChoice = safeInputInt("请选择操作", 0, 3);
|
|
||||||
switch (courseChoice) {
|
|
||||||
case 1: // 修改现有课程成绩
|
|
||||||
if (students[i].courseCount == 0) {
|
|
||||||
printWarning("该学生没有课程记录!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int courseIndex = safeInputInt("请选择要修改的课程", 1, students[i].courseCount) - 1;
|
|
||||||
students[i].scores[courseIndex] = safeInputFloat("新成绩", MIN_SCORE, MAX_SCORE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // 添加新课程
|
|
||||||
if (students[i].courseCount >= MAX_COURSES) {
|
|
||||||
printWarning("课程数量已达上限!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
safeInputString("课程名称", students[i].courses[students[i].courseCount], MAX_COURSE_NAME_LENGTH);
|
|
||||||
students[i].scores[students[i].courseCount] = safeInputFloat("课程成绩", MIN_SCORE, MAX_SCORE);
|
|
||||||
students[i].courseCount++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // 删除课程
|
|
||||||
if (students[i].courseCount == 0) {
|
|
||||||
printWarning("该学生没有课程记录!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
courseIndex = safeInputInt("请选择要删除的课程", 1, students[i].courseCount) - 1;
|
|
||||||
|
|
||||||
// 移动数组元素
|
|
||||||
for (int k = courseIndex; k < students[i].courseCount - 1; k++) {
|
|
||||||
strcpy(students[i].courses[k], students[i].courses[k + 1]);
|
|
||||||
students[i].scores[k] = students[i].scores[k + 1];
|
|
||||||
}
|
|
||||||
students[i].courseCount--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (choice == 4) {
|
|
||||||
calculateStudentStats(&students[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
dataModified = true;
|
|
||||||
statsNeedUpdate = true;
|
|
||||||
|
|
||||||
printSuccess("学生信息修改成功!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printError("未找到该学号的学生!");
|
|
||||||
pauseSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 修改学生课程成绩
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @brief 按学号查找学生
|
|
||||||
*/
|
|
||||||
void searchStudentByID() {
|
|
||||||
clearScreen();
|
|
||||||
printHeader("按学号查找学生");
|
|
||||||
|
|
||||||
if (studentCount == 0) {
|
|
||||||
printWarning("暂无学生数据!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char studentID[MAX_ID_LENGTH];
|
|
||||||
printf("\n");
|
|
||||||
safeInputString("请输入学号", studentID, MAX_ID_LENGTH);
|
|
||||||
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
if (strcmp(students[i].studentID, studentID) == 0) {
|
|
||||||
displayStudentInfo(&students[i]);
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printError("未找到该学号的学生!");
|
|
||||||
pauseSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 按姓名查找学生
|
|
||||||
*/
|
|
||||||
void searchStudentByName() {
|
|
||||||
clearScreen();
|
|
||||||
printHeader("按姓名查找学生");
|
|
||||||
|
|
||||||
if (studentCount == 0) {
|
|
||||||
printWarning("暂无学生数据!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char name[MAX_NAME_LENGTH];
|
|
||||||
printf("\n");
|
|
||||||
safeInputString("请输入姓名(支持模糊查找)", name, MAX_NAME_LENGTH);
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
if (strstr(students[i].name, name) != NULL) {
|
|
||||||
if (!found) {
|
|
||||||
printf("\n找到以下匹配的学生:\n");
|
|
||||||
printSeparator();
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
displayStudentInfo(&students[i]);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
printError("未找到匹配的学生!");
|
|
||||||
}
|
|
||||||
|
|
||||||
pauseSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示所有学生信息
|
|
||||||
*/
|
|
||||||
void displayAllStudents() {
|
|
||||||
clearScreen();
|
|
||||||
printHeader("所有学生信息");
|
|
||||||
|
|
||||||
if (studentCount == 0) {
|
|
||||||
printWarning("暂无学生数据!");
|
|
||||||
pauseSystem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
// 调整中文表头的对齐格式,考虑中文字符的显示宽度
|
|
||||||
printf("%-10s %-12s %-6s %-6s %-8s %-8s\n",
|
|
||||||
"学号", "姓名", "年龄", "性别", "总分", "平均分");
|
|
||||||
printf("========================================\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < studentCount; i++) {
|
|
||||||
printf("%-10s %-12s %-6d %-6c %-8.2f %-8.2f\n",
|
|
||||||
students[i].studentID,
|
|
||||||
students[i].name,
|
|
||||||
students[i].age,
|
|
||||||
students[i].gender,
|
|
||||||
students[i].totalScore,
|
|
||||||
students[i].averageScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n总学生数: %d\n", studentCount);
|
|
||||||
pauseSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示单个学生详细信息
|
|
||||||
*/
|
|
||||||
void displayStudentInfo(const Student* student) {
|
|
||||||
printf("\n学生详细信息:\n");
|
|
||||||
printSeparator();
|
|
||||||
printf("学号: %s\n", student->studentID);
|
|
||||||
printf("姓名: %s\n", student->name);
|
|
||||||
printf("年龄: %d\n", student->age);
|
|
||||||
printf("性别: %c\n", student->gender);
|
|
||||||
printf("课程数量: %d\n", student->courseCount);
|
|
||||||
|
|
||||||
if (student->courseCount > 0) {
|
|
||||||
printf("\n课程成绩:\n");
|
|
||||||
for (int i = 0; i < student->courseCount; i++) {
|
|
||||||
printf(" %s: %.2f分\n",
|
|
||||||
student->courses[i],
|
|
||||||
student->scores[i]);
|
|
||||||
}
|
|
||||||
printf("\n总分: %.2f\n", student->totalScore);
|
|
||||||
printf("平均分: %.2f\n", student->averageScore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 排序学生信息
|
|
||||||
*/
|
|
||||||
void sortStudents(int criteria, int order) {
|
|
||||||
if (studentCount <= 1) return;
|
|
||||||
|
|
||||||
// 使用冒泡排序
|
|
||||||
for (int i = 0; i < studentCount - 1; i++) {
|
|
||||||
for (int j = 0; j < studentCount - 1 - i; j++) {
|
|
||||||
bool shouldSwap = false;
|
|
||||||
|
|
||||||
switch (criteria) {
|
|
||||||
case SORT_BY_ID:
|
|
||||||
shouldSwap = (order == SORT_ASCENDING) ?
|
|
||||||
strcmp(students[j].studentID, students[j + 1].studentID) > 0 :
|
|
||||||
strcmp(students[j].studentID, students[j + 1].studentID) < 0;
|
|
||||||
break;
|
|
||||||
case SORT_BY_NAME:
|
|
||||||
shouldSwap = (order == SORT_ASCENDING) ?
|
|
||||||
strcmp(students[j].name, students[j + 1].name) > 0 :
|
|
||||||
strcmp(students[j].name, students[j + 1].name) < 0;
|
|
||||||
break;
|
|
||||||
case SORT_BY_TOTAL_SCORE:
|
|
||||||
shouldSwap = (order == SORT_ASCENDING) ?
|
|
||||||
students[j].totalScore > students[j + 1].totalScore :
|
|
||||||
students[j].totalScore < students[j + 1].totalScore;
|
|
||||||
break;
|
|
||||||
case SORT_BY_AVERAGE_SCORE:
|
|
||||||
shouldSwap = (order == SORT_ASCENDING) ?
|
|
||||||
students[j].averageScore > students[j + 1].averageScore :
|
|
||||||
students[j].averageScore < students[j + 1].averageScore;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldSwap) {
|
|
||||||
Student temp = students[j];
|
|
||||||
students[j] = students[j + 1];
|
|
||||||
students[j + 1] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataModified = true;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user