跳至内容

学生成绩管理系统

你已经掌握了基本的c语言的语法知识,也已经在Visual Studio 2022写了这九个代码文件,但是有些功能还没有完善。我会把需求告诉你,你来完善。你现在需要记住这个学生成绩管理系统和这七个我已经写的文件。记住后恢复“我已经记住了”

需求

完成 1.成绩输出时需要格式化输出。按照学号班级姓名成绩的方式,对齐的成绩表格来输出。
完成 2.成绩预测方法有问题。请你完善。(简单线性回归算法)
完成 3.添加颜色输出(使用 ANSI 转义序列)
完成 4.支持批量导入 / 导出成绩数据
5.实现分页显示大量学生信息
6.实现多班级管理(使用二维数组或链表嵌套)
7.实现成绩的快速统计(如区间分数段统计)
完成	8.查看所有任务
9.班级平均分

*学生成绩管理系统*

*问题描述*

设计一个简易的学生成绩管理系统,实现学生信息和课程成绩的录入、查询、统计和分析功能。系统需使用数组存储学生基本信息,使用链表管理课程成绩,并通过队列实现优先级任务处理,帮助教师高效管理班级成绩数据。

*功能要求*

1. *数据结构设计*

o 使用****结构体数组****存储学生基本信息(学号、姓名、性别等)

o 使用****链表****管理每个学生的多门课程成绩(动态添加课程)

o 使用****队列****实现优先级任务(如紧急成绩修改请求优先处理)

2. *核心功能实现*

o *学生信息管理*:添加、删除、修改学生基本信息

o *成绩管理*:录入、查询、修改课程成绩

o *统计分析*:计算平均分、最高分、最低分、排名等

o *优先级任务*:处理不同紧急程度的成绩管理任务

3. *算法要求*

o 实现学生信息的排序(如按学号、平均分排序)

o 实现二分查找(针对已排序的学生数组)

o 实现队列的入队 / 出队操作(优先级任务处理)

4. *用户界面设计*

o 设计命令行交互菜单

o 表格形式展示学生成绩

o 错误处理(如输入非法学号、重复添加学生)

5. *文件操作*

o 实现学生数据的持久化存储(读取 / 保存到文件)

o 支持成绩修改记录的日志功能

*技术要求*

  1. 必须使用 C 语言实现,合理使用:

o 数组(存储学生基本信息)

o 链表(动态管理课程成绩)

o 队列(优先级任务处理)

  1. 代码需具备良好的模块化设计,例如:

o student.h/student.c:学生信息管理

o score.h/score.c:成绩链表管理

o task.h/task.c:优先级任务队列

o main.c:主程序与用户界面

  1. 需包含必要的错误处理(如内存分配失败、文件操作错误)

*推荐实现步骤*

1. *需求分析与设计*(2 学时)

o 设计数据结构(学生结构体、成绩链表、任务队列)

o 规划功能模块与交互流程

2. *基础数据结构实现*(4 学时)

o 实现学生结构体与数组操作(增删改查)

o 实现成绩链表(插入、删除节点)

o 实现优先级队列(入队、出队)

3. *核心功能开发*(6 学时)

o 实现学生信息管理功能

o 实现成绩录入与查询功能

o 实现统计分析功能(平均分、排名等)

o 实现优先级任务处理(如紧急成绩修改)

4. *用户界面与文件操作*(4 学时)

o 设计命令行菜单与输入处理

o 实现数据的文件读写

o 优化输出格式(如对齐的成绩表格)

*评分标准*

1. *功能完整性*(30 分):是否实现核心功能(学生管理、成绩管理、统计分析)

2. *数据结构合理性*(25 分):数组、链表、队列的使用是否恰当

3. *算法正确性*(20 分):排序、查找、队列操作是否正确

4. *代码质量*(15 分):模块化设计、注释、错误处理

5. *用户体验*(10 分):界面友好性、错误提示

*扩展建议*

1. *高级功能*

o 实现多班级管理(使用二维数组或链表嵌套)

o 添加成绩预测功能(简单线性回归算法)

o 支持批量导入 / 导出成绩数据

2. *算法优化*

o 使用哈希表优化学生查询效率

o 实现成绩的快速统计(如区间分数段统计)

3. *用户界面*

o 添加颜色输出(使用 ANSI 转义序列)

o 实现分页显示大量学生信息


student.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#pragma once
#ifndef STUDENT_H
#define STUDENT_H

#include <stdbool.h>
#include "score.h"

#define MAX_STUDENTS 1000
#define NAME_LEN 32
#define CLASS_LEN 16

//学生结构体定义
typedef struct
{
	long long id;               // 学号
	char name[NAME_LEN];        // 姓名
	char gender[4];				// "男" 或 "女"
	char class_name[CLASS_LEN]; // 班级
	ScoreNode* scores;			// 指向该学生成绩链表头
} Student;

//哈希表定义
typedef struct HashEntry {
	long long id;
	Student* student;
	struct HashEntry* next;
} HashEntry;

extern Student students[MAX_STUDENTS];
extern int student_count;

// 全局哈希表声明
extern HashEntry** hash_table;
extern int hash_size;

// 学生信息操作
bool add_student(long long id, const char* name, const char* gender, const char* class_name);
bool delete_student(long long id);
Student* find_student(long long id);
bool update_student(long long id, const char* new_name, const char* new_gender, const char* new_class);

// 排序与查询
void sort_students_by_id();
void sort_students_by_avg_score();
Student* binary_search_student(long long id);
Student* hash_find_student(long long id);

// 文件持久化
bool load_students(const char* filename);
bool save_students(const char* filename);

// 哈希表管理函数
void init_hash_table();
void free_hash_table();
void hash_add_student(long long id, const char* name, const char* gender, const char* class_name, ScoreNode* scores);
void hash_delete_student(long long id);

#endif // STUDENT_H

student.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#define _CRT_SECURE_NO_WARNINGS
#include "student.h"
#include "score.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "color.h"

#define DEFAULT_HASH_SIZE 1024

Student students[MAX_STUDENTS];
int student_count = 0;

// 哈希表全局变量
HashEntry** hash_table = NULL;
int hash_size = 0;

bool add_student(long long id, const char* name, const char* gender, const char* class_name)
{
	/*
		负责向系统中添加新的学生
	*/
	if (student_count >= MAX_STUDENTS)
		return false;
	if (find_student(id))
		return false;
	Student* s = &students[student_count++];
	s->id = id;
	strncpy(s->name, name, NAME_LEN);
	strncpy(s->gender, gender, sizeof(s->gender));
	strncpy(s->class_name, class_name, CLASS_LEN);
	s->scores = NULL;
	return true;
}

bool delete_student(long long id)
{
	for (int i = 0; i < student_count; i++)
	{
		if (students[i].id == id)
		{
			free_score_list(students[i].scores);
			students[i] = students[--student_count];
			return true;
		}
	}
	return false;
}

Student* find_student(long long id)
{
	for (int i = 0; i < student_count; i++)
		if (students[i].id == id)
			return &students[i];
	return NULL;
}

bool update_student(long long id, const char* new_name, const char* new_gender, const char* new_class)
{
	Student* s = find_student(id);
	if (!s) {
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return false;
	}
	if (new_name)
		strncpy(s->name, new_name, NAME_LEN);
	if (new_gender)
		strncpy(s->gender, new_gender, sizeof(s->gender));
	if (new_class)
		strncpy(s->class_name, new_class, CLASS_LEN);
	return true;
}

static void swap_student(Student* a, Student* b)
{
	Student tmp = *a;
	*a = *b;
	*b = tmp;
}

void sort_students_by_id()
{
	for (int i = 0; i < student_count - 1; i++)
		for (int j = 0; j < student_count - i - 1; j++)
			if (students[j].id > students[j + 1].id)
				swap_student(&students[j], &students[j + 1]);
}

void sort_students_by_avg_score()
{
	for (int i = 0; i < student_count - 1; i++)
		for (int j = 0; j < student_count - i - 1; j++)
		{
			double a = calc_avg_score(students[j].scores);
			double b = calc_avg_score(students[j + 1].scores);
			if (a < b)
				swap_student(&students[j], &students[j + 1]);
		}
}

Student* binary_search_student(long long id)
{
	int lo = 0, hi = student_count - 1;
	while (lo <= hi)
	{
		int mid = (lo + hi) / 2;
		if (students[mid].id == id)
			return &students[mid];
		else if (students[mid].id < id)
			lo = mid + 1;
		else
			hi = mid - 1;
	}
	return NULL;
}

bool load_students(const char* filename)
{
	FILE* fp = fopen(filename, "rb");
	if (!fp) {
		printf(COLOR_RED "无法打开文件 %s\n" COLOR_RESET, filename);
		return false;
	}

	if (fread(&student_count, sizeof(int), 1, fp) != 1) {
		fclose(fp);
		return false;
	}

	// 如果students已有数据,先释放链表
	for (int i = 0; i < student_count; i++) {
		free_score_list(students[i].scores);
		students[i].scores = NULL;
	}

	for (int i = 0; i < student_count; i++)
	{
		if (fread(&students[i].id, sizeof(long long), 1, fp) != 1) goto load_error;
		if (fread(students[i].name, sizeof(char), NAME_LEN, fp) != NAME_LEN) goto load_error;
		if (fread(students[i].gender, sizeof(char), sizeof(students[i].gender), fp) != sizeof(students[i].gender)) goto load_error;
		if (fread(students[i].class_name, sizeof(char), CLASS_LEN, fp) != CLASS_LEN) goto load_error;

		int count;
		if (fread(&count, sizeof(int), 1, fp) != 1) goto load_error;

		students[i].scores = NULL;
		for (int k = 0; k < count; k++)
		{
			char course[32];
			double grade;
			if (fread(course, sizeof(char), 32, fp) != 32) goto load_error;
			if (fread(&grade, sizeof(double), 1, fp) != 1) goto load_error;
			students[i].scores = add_score(students[i].scores, course, grade);
		}
	}

	fclose(fp);
	return true;

load_error:
	printf(COLOR_RED "文件读取错误或文件损坏\n" COLOR_RESET);
	fclose(fp);
	return false;
}

bool save_students(const char* filename)
{
	FILE* fp = fopen(filename, "wb");
	if (!fp) {
		printf(COLOR_RED "无法打开文件 %s" COLOR_RESET "\n", filename);
		return false;
	}

	// 写入学生数量
	if (fwrite(&student_count, sizeof(int), 1, fp) != 1) {
		printf(COLOR_RED "写入学生数量失败\n" COLOR_RESET);
		fclose(fp);
		return false;
	}

	for (int i = 0; i < student_count; i++) {
		Student* s = &students[i];

		if (fwrite(&s->id, sizeof(long long), 1, fp) != 1) goto write_error;
		if (fwrite(s->name, sizeof(char), NAME_LEN, fp) != NAME_LEN) goto write_error;
		if (fwrite(s->gender, sizeof(char), sizeof(s->gender), fp) != sizeof(s->gender)) goto write_error;
		if (fwrite(s->class_name, sizeof(char), CLASS_LEN, fp) != CLASS_LEN) goto write_error;

		// 先统计课程数
		int count = 0;
		for (ScoreNode* p = s->scores; p; p = p->next) count++;

		if (fwrite(&count, sizeof(int), 1, fp) != 1) goto write_error;

		// 写课程成绩
		for (ScoreNode* p = s->scores; p; p = p->next) {
			if (fwrite(p->course, sizeof(char), 32, fp) != 32) goto write_error;
			if (fwrite(&p->grade, sizeof(double), 1, fp) != 1) goto write_error;
		}
	}

	fclose(fp);
	return true;

write_error:
	printf(COLOR_RED "写入文件 %s 失败\n" COLOR_RESET, filename);
	fclose(fp);
	return false;
}

// 哈希函数
unsigned int hash_function(long long id) {
	return abs((int)(id % hash_size));
}

// 初始化哈希表
void init_hash_table() {
	if (hash_table) free_hash_table();

	hash_size = student_count;
	hash_table = (HashEntry**)malloc(hash_size * sizeof(HashEntry*));
	if (!hash_table) {
		fprintf(stderr, " 内存分配失败\n");
		exit(EXIT_FAILURE);
	}

	for (int i = 0; i < hash_size; i++)
		hash_table[i] = NULL;
}

// 添加学生到哈希表
void add_to_hash(Student* student) {
	unsigned int index = hash_function(student->id);

	HashEntry* entry = (HashEntry*)malloc(sizeof(HashEntry));
	if (!entry) {
		fprintf(stderr, " 内存分配失败\n");
		return;
	}

	entry->id = student->id;
	entry->student = student;
	entry->next = hash_table[index];
	hash_table[index] = entry;
}

// 从哈希表查找学生
Student* hash_find_student(long long id) {
	unsigned int index = hash_function(id);

	for (HashEntry* entry = hash_table[index]; entry; entry = entry->next) {
		if (entry->id == id)
			return entry->student;
	}
	return NULL;
}

// 从哈希表移除学生
void remove_from_hash(long long id) {
	unsigned int index = hash_function(id);

	HashEntry dummy = { .next = hash_table[index] };
	HashEntry* prev = &dummy;

	while (prev->next) {
		if (prev->next->id == id) {
			HashEntry* del = prev->next;
			prev->next = del->next;
			free(del);
			break;
		}
		prev = prev->next;
	}

	hash_table[index] = dummy.next;
}

// 释放哈希表内存
void free_hash_table() {
	if (!hash_table) return;

	for (int i = 0; i < hash_size; i++) {
		HashEntry* entry = hash_table[i];
		while (entry) {
			HashEntry* next = entry->next;
			free(entry);
			entry = next;
		}
	}

	free(hash_table);
	hash_table = NULL;
	hash_size = 0;
}



// 学生管理函数实现
void hash_add_student(long long id, const char* name, const char* gender, const char* class_name, ScoreNode* scores) {
	// 验证学号唯一性
	if (hash_find_student(id)) {
		printf("学号 %lld 已存在\n", id);
		return;
	}

	// 创建学生对象
	Student* student = (Student*)malloc(sizeof(Student));
	if (!student) {
		fprintf(stderr, "内存分配失败\n");
		return;
	}

	student->id = id;
	strncpy(student->name, name, sizeof(student->name) - 1);
	strncpy(student->gender, gender, sizeof(student->gender) - 1);
	strncpy(student->class_name, class_name, sizeof(student->class_name) - 1);
	student->scores = scores;

	// 添加到哈希表
	add_to_hash(student);

	printf(COLOR_GREEN " 成功添加学生: %s (ID: %lld)\n" COLOR_RESET, name, id);
}

void hash_delete_student(long long id) {
	Student* student = hash_find_student(id);
	if (!student) {
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}

	remove_from_hash(id);
	free(student);

	printf(COLOR_GREEN "成功删除学号 %lld 的学生\n" COLOR_RESET, id);
}

score.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#pragma once
#ifndef SCORE_H
#define SCORE_H

typedef struct ScoreNode
{
	char course[32];
	double grade;
	struct ScoreNode* next;
} ScoreNode;

// 链表操作
ScoreNode* add_score(ScoreNode* head, const char* course, double grade);
ScoreNode* remove_score(ScoreNode* head, const char* course);
ScoreNode* find_score(ScoreNode* head, const char* course);
void free_score_list(ScoreNode* head);

// 统计分析
double calc_avg_score(ScoreNode* head);
double calc_max_score(ScoreNode* head);
double calc_min_score(ScoreNode* head);

//成绩预测
double predict_score(ScoreNode* scores, const char* course);	//

#endif // SCORE_H

score.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "score.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "color.h"

ScoreNode* add_score(ScoreNode* head, const char* course, double grade)
{
	ScoreNode* node = (ScoreNode*)malloc(sizeof * node);
	if (!node)
	{
		perror("malloc");
		return head;
	}
	strncpy(node->course, course, sizeof(node->course));
	node->grade = grade;
	node->next = head;
	return node;
}

ScoreNode* remove_score(ScoreNode* head, const char* course)
{
	ScoreNode dummy = { .next = head };
	ScoreNode* prev = &dummy;
	while (prev->next)
	{
		if (strcmp(prev->next->course, course) == 0)
		{
			ScoreNode* del = prev->next;
			prev->next = del->next;
			free(del);
			break;
		}
		prev = prev->next;
	}
	return dummy.next;
}

ScoreNode* find_score(ScoreNode* head, const char* course)
{
	for (; head; head = head->next)
		if (strcmp(head->course, course) == 0)
			return head;
	return NULL;
}

void free_score_list(ScoreNode* head)
{
	while (head)
	{
		ScoreNode* t = head;
		head = head->next;
		free(t);
	}
}

double calc_avg_score(ScoreNode* head)
{
	if (!head)
		return 0.0;
	double sum = 0;
	int cnt = 0;
	for (ScoreNode* p = head; p; p = p->next)
	{
		sum += p->grade;
		cnt++;
	}
	return cnt ? sum / cnt : 0.0;
}

double calc_max_score(ScoreNode* head)
{
	if (!head)
		return 0.0;
	double m = head->grade;
	for (head = head->next; head; head = head->next)
		if (head->grade > m)
			m = head->grade;
	return m;
}

double calc_min_score(ScoreNode* head)
{
	if (!head)
		return 0.0;
	double m = head->grade;
	for (head = head->next; head; head = head->next)
		if (head->grade < m)
			m = head->grade;
	return m;
}

double predict_score(ScoreNode* scores, const char* course) {
	if (!scores) return 0;

	double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0;
	int count = 0;

	// 遍历所有课程成绩
	for (ScoreNode* p = scores; p; p = p->next) {
		sum_x += count;					// x 为课程序号(时间序列)
		sum_y += p->grade;				// y 为成绩
		sum_xy += count * p->grade;
		sum_x2 += count * count;
		count++;
	}

	if (count < 2){						// 至少需要两个数据点
		puts("提示:至少需要两个成绩数据才能进行预测");
		return 0.0;
	}

	double x_avg = sum_x / count;
	double y_avg = sum_y / count;

	// 计算斜率和截距
	double numerator = sum_xy - count * x_avg * y_avg;
	double denominator = sum_x2 - count * x_avg * x_avg;

	if (denominator == 0) return y_avg;  // 避免除以零

	double slope = numerator / denominator;
	double intercept = y_avg - slope * x_avg;

	// 预测下一个成绩(假设下一次课程序号为 count)
	double prediction = intercept + slope * count;

	// 限制预测值在 0 ~ 100 之间
	prediction = fmax(0.0, fmin(100.0, prediction));

	// 四舍五入到两位小数
	prediction = round(prediction * 100.0) / 100.0;

	return prediction;
}

task.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#pragma once
#ifndef TASK_H
#define TASK_H

typedef enum
{
	HIGH = 1,
	MEDIUM = 2,
	LOW = 3
} Priority;

typedef struct Task
{
	long long student_id;
	char description[64];
	Priority prio;
} Task;

// 优先级队列,使用链表按 prio 降序插入
typedef struct TaskNode
{
	Task task;
	struct TaskNode* next;
} TaskNode;

void enqueue_task(TaskNode** head, Task t);
TaskNode* dequeue_task(TaskNode** head);
void free_task_queue(TaskNode* head);
void print_all_tasks(TaskNode* head);

#endif // TASK_H

task.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#define _CRT_SECURE_NO_WARNINGS
#include "task.h"
#include <stdio.h>
#include <stdlib.h>
#include "color.h"

void enqueue_task(TaskNode** head, Task t)
{
	TaskNode* node = (TaskNode*)malloc(sizeof * node);
	if (!node)
	{
		perror("malloc");
		return;
	}
	node->task = t;
	node->next = NULL;
	if (!*head || t.prio < (*head)->task.prio)
	{
		node->next = *head;
		*head = node;
	}
	else
	{
		TaskNode* p = *head;
		while (p->next && p->next->task.prio <= t.prio)
			p = p->next;
		node->next = p->next;
		p->next = node;
	}
}

TaskNode* dequeue_task(TaskNode** head)
{
	if (!*head)
		return NULL;
	TaskNode* top = *head;
	*head = top->next;
	return top;
}

void free_task_queue(TaskNode* head)
{
	while (head)
	{
		TaskNode* t = head;
		head = head->next;
		free(t);
	}
}

void print_all_tasks(TaskNode* head) {
	if (!head) {
		printf(COLOR_YELLOW "当前任务队列为空\n" COLOR_RESET);
		return;
	}

	int count = 1;
	printf("\n\033[34m=== 任务列表 (按优先级排序) ===\033[0m\n");
	while (head) {
		char* priority_str = (head->task.prio == HIGH) ? "高" :
			(head->task.prio == MEDIUM) ? "中" : "低";
		printf("\033[36m%d. 学号 %lld | 优先级 %s | 描述: %s\033[0m\n",
			count++, head->task.student_id, priority_str, head->task.description);
		head = head->next;
	}
	printf("\n");
}

main.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "student.h"
#include "score.h"
#include "task.h"
#include "batch_ops.h"
#include "print_util.h" 
#include "color.h"


static TaskNode* task_queue = NULL;     //任务队列的头指针

void menu_sys();
void menu_stu();
void menu_score();
void menu_task();
void handle_add_student();
void handle_delete_student();
void handle_update_student();
void handle_add_score();
void handle_modify_score();
void handle_stats();
void handle_task();
void handle_predict_score();
void hash_search();
void order_search();
void binary_search();
void print_all_student();
void handle_import_scores();
void handle_export_scores();
void initialize_all();
void print_score_table_formatted();

int main()
{
	char ch;
	printf("是否需要载入历史数据(Y or N)\n");
	scanf(" %c", &ch); 

	if (ch == 'Y' || ch == 'y')  
	{
		printf(COLOR_GREEN "正在加载数据.....\n" COLOR_RESET);
		load_students("students.dat");   
	}
	else
	{
		initialize_all(); 
	}
	
	int choice1, choice2, choice3;
	while (1)
	{
		menu_sys();
		printf("请选择> ");
		if (scanf("%d", &choice1) != 1)
			break;
		switch (choice1)
		{
		case 1:
			menu_stu();
			printf("请选择> ");
			if (scanf("%d", &choice2) != 1)
				break;
			switch (choice2)
			{
			case 1:
				handle_add_student();
				break;
			case 2:
				handle_delete_student();
				break;
			case 3:
				handle_update_student();
				break;
			case 4:
				print_all_student();
				break;
			case 5:
				order_search();
				break;
			case 6:
				binary_search();
				break;
			case 7:
				hash_search();
				break;
			case 0:
				break;
			default:
				printf(COLOR_RED "无效选项,请重新输入!\n" COLOR_RESET);
			}
			break;
		case 2:
			menu_score();
			printf("请选择> ");
			if (scanf("%d", &choice3) != 1)
				break;
			switch (choice3)
			{
			case 1:
				handle_add_score();
				break;
			case 2:
				handle_modify_score();
				break;
			case 3:
				handle_stats();
				break;
			case 4:
				handle_predict_score();
				break;
			case 5:
				print_score_table_formatted();
				break;
			case 0:
				break;
			default:
				printf(COLOR_RED "无效选项,请重新输入!\n" COLOR_RESET);
			}
			break;
		case 3:
			menu_task();
			handle_task();
			break;
		case 4:								//批量导入
			handle_import_scores();
			break;
		case 5:								//批量导出
			handle_export_scores();
			break;
		case 6:
			initialize_all();
			break;
		case 0:
			printf(COLOR_GREEN "文件保存在students.dat 中\n正在退出程序...\n" COLOR_RESET);
			save_students("students.dat");	// 保存学生数据到文件
			free_score_list(NULL);			// 释放成绩链表(这里传入NULL是因为已经在其他地方释放)
			free_task_queue(task_queue);
			return 0;
		default:
			printf(COLOR_RED "无效选项,请重新输入!\n" COLOR_RESET);
		}
	}
	return 0;
}

void menu_sys()
{
	puts("\n======== 学生成绩管理系统 ========");
	puts("1. 学生管理系统");
	puts("2. 成绩管理系统");
	puts("3. 任务管理系统");
	puts("4. 批量导入");
	puts("5. 批量导出");
	puts("6. 初始化所有数据");
	puts("0. 退出并保存");
}

void menu_stu()
{
	puts("\n======== 学生管理系统 ========");
	puts("1. 添加学生");
	puts("2. 删除学生");
	puts("3. 修改学生");
	puts("4. 输出所有学生信息");
	puts("5. 查询学生信息(顺序查找)");
	puts("6. 查询学生信息(二分查找)");
	puts("7. 查询学生信息(哈希查找)");
	puts("0. 返回上一级");
}

void menu_score()
{
	puts("\n======== 成绩管理系统 ========");
	puts("1. 录入成绩");
	puts("2. 修改成绩");
	puts("3. 统计分析");
	puts("4. 成绩预测");
	puts("5. 班级成绩输出");
	puts("0. 返回上一级");
}

void menu_task()
{
	puts("\n======== 任务管理系统 ========");
}

void handle_add_student()
{
	long long id;
	char name[32], gender[4], cls[16];

	printf("学号: ");
	if (scanf("%lld", &id) != 1)
	{
		printf(COLOR_RED "学号输入错误(示例:2024xxxxxxx)" COLOR_RESET);
		return;
	}
	printf("姓名: ");
	scanf("%s", name);
	printf("性别(男/女): ");
	scanf("%s", gender);
	printf("班级: ");
	scanf("%s", cls);
	if (add_student(id, name, gender, cls))
		printf(COLOR_GREEN "学生添加成功。" COLOR_RESET);
	else
		printf(COLOR_RED "添加失败(重复/已满)" COLOR_RESET);
}

void handle_delete_student()
{
	long long id;
	printf("学号: ");
	if (scanf("%lld", &id) != 1)
	{
		printf(COLOR_RED "学号输入错误(示例:2024xxxxxxx)" COLOR_RESET);
		return;
	}
	if (delete_student(id))
		printf(COLOR_GREEN "删除成功。" COLOR_RESET);
	else
		printf(COLOR_RED "删除失败" COLOR_RESET);
}

void handle_update_student()
{
	long long id;
	char name_buf[64] = { 0 }, gender_buf[8] = { 0 }, cls_buf[32] = { 0 };
	char* new_name = NULL;
	char* new_gender = NULL;
	char* new_class = NULL;

	printf("学号: ");
	if (scanf("%lld", &id) != 1) {
		printf(COLOR_RED "学号输入错误(示例:2024xxxxxxx)" COLOR_RESET);
		
		// 清掉行尾残留
		int ch; while ((ch = getchar()) != '\n' && ch != EOF);
		return;
	}
	// 清掉换行,为下面 fgets 做准备
	int ch; 
	while ((ch = getchar()) != '\n' && ch != EOF);

	// 用 fgets 读取整行,包括空行
	printf("新姓名(回车跳过): ");
	fgets(name_buf, sizeof(name_buf), stdin);
	if (name_buf[0] != '\n') {
		// 去掉末尾换行
		name_buf[strcspn(name_buf, "\n")] = '\0';
		new_name = name_buf;
	}

	printf("新性别(男/女)(回车跳过): ");
	fgets(gender_buf, sizeof(gender_buf), stdin);
	if (gender_buf[0] != '\n') {
		gender_buf[strcspn(gender_buf, "\n")] = '\0';
		new_gender = gender_buf;
	}

	printf("新班级(回车跳过): ");
	fgets(cls_buf, sizeof(cls_buf), stdin);
	if (cls_buf[0] != '\n') {
		cls_buf[strcspn(cls_buf, "\n")] = '\0';
		new_class = cls_buf;
	}

	if (update_student(id, new_name, new_gender, new_class))
	{
		printf(COLOR_GREEN "更新成功,新信息如下:\n" COLOR_RESET);
		printf("学号:%lld  姓名:%s  性别:%s  班级:%s\n", id, new_name, new_gender, new_class);
	}
	else
		printf(COLOR_RED "更新失败" COLOR_RESET);
}

void handle_add_score()
{
	long long id;
	char course[32];
	double grade;
	printf("学号: ");
	if (scanf("%lld", &id) != 1)
	{
		printf(COLOR_RED "学号输入错误(示例:2024xxxxxxx)" COLOR_RESET);
		return;
	}
	Student* s = find_student(id);
	if (!s)
	{
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}
	printf("课程: ");
	scanf("%s", course);
	printf("成绩: ");
	scanf("%lf", &grade);
	s->scores = add_score(s->scores, course, grade);
	printf(COLOR_GREEN "成绩录入完成" COLOR_RESET);
}

void handle_modify_score()
{
	long long id;
	char course[32];
	double grade;
	printf("学号: ");
	if (scanf("%lld", &id) != 1)
	{
		printf(COLOR_RED "学号输入错误(示例:2024xxxxxxx)" COLOR_RESET);
		return;
	}
	Student* s = find_student(id);
	if (!s)
	{
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}
	printf("课程: ");
	scanf("%s", course);
	ScoreNode* node = find_score(s->scores, course);
	if (!node)
	{
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}
	printf("新成绩: ");
	scanf("%lf", &grade);
	node->grade = grade;
	FILE* logf = fopen("modify.log", "a");
	if (logf)
	{
		fprintf(logf, "%lld,%s,%.2f\n", id, course, grade);
		fclose(logf);
	}
	printf(COLOR_GREEN "成绩修改完成\n" COLOR_RESET);
}

void handle_stats()
{
	sort_students_by_avg_score();
	puts("按平均分降序排序,前五名:");
	for (int i = 0; i < student_count && i < 5; i++)
	{
		Student* s = &students[i];
		printf("%d. %lld %s 平均:%.2f 最高:%.2f 最低:%.2f\n",
			i + 1, s->id, s->name,
			calc_avg_score(s->scores),
			calc_max_score(s->scores),
			calc_min_score(s->scores));
	}
}

void handle_task()
{
	int op;
	puts("1. 新建任务");
	puts("2. 处理下一个");
	puts("3. 查看所有任务");
	puts("0. 返回上一级");
	printf("请选择> ");
	scanf("%d", &op);
	if (op == 1)
	{
		Task t;
		printf("学号: ");
		scanf("%lld", &t.student_id);
		printf("描述: ");
		scanf(" %[^\n]", t.description);
		printf("优先级(1高2中3低): ");
		scanf("%d", (int*)&t.prio);
		enqueue_task(&task_queue, t);
		printf(COLOR_GREEN "已入队。" COLOR_RESET);
	}
	else if (op == 2)
	{
		TaskNode* node = dequeue_task(&task_queue);
		if (!node)
			printf(COLOR_YELLOW "队列为空" COLOR_RESET);
		else
		{
			printf("处理: %lld\n描述:%s\n优先级:%d\n",
				node->task.student_id,
				node->task.description,
				node->task.prio);
			free(node);
		}
	}
	else if (op == 3)
	{
		print_all_tasks(task_queue);
	}
	else if (op == 0)
	{
	}
}

void handle_predict_score()
{
	long long student_id;
	char target_course[50];
	double predicted;

	printf("请输入要预测成绩的学生学号: ");
	scanf("%lld", &student_id);
	Student* s = find_student(student_id);
	if (!s) {
		printf(COLOR_RED "学生不存在。\n" COLOR_RESET);
		return;
	}

	printf("请输入要预测的课程名称: ");
	scanf("%s", target_course);

	// 调用性回归模型预测成绩
	predicted = predict_score(s->scores, target_course);
	if (predicted == 0) {
		printf(COLOR_YELLOW "无法预测该课程成绩,请添加至少两门相关课程的成绩。\n" COLOR_RESET);
		return;
	}
	printf(COLOR_GREEN "%s预测结果为%.2lf" COLOR_RESET "\n", target_course, predicted);

}

void hash_search()
{
	long long student_id;

	printf("请输入学生学号: ");
	scanf("%lld", &student_id);

	//初始化哈希表
	init_hash_table();

	//构建我的哈希表
	for (int i = 0; i < student_count; i++)
		hash_add_student(students[i].id, students[i].name, students[i].gender, students[i].class_name, students[i].scores);

	Student* s = hash_find_student(student_id);

	if (!s) {
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}

	printf(COLOR_GREEN " 找到学号 %lld 的学生" COLOR_RESET "\n", student_id);


	print_students_table(s);

	//销毁哈希表
	free_hash_table();
}

void order_search()
{
	long long student_id;

	printf("请输入学生学号: ");
	scanf("%lld", &student_id);

	Student* s = find_student(student_id);

	if (!s) {
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}

	printf(COLOR_GREEN " 找到学号 %lld 的学生" COLOR_RESET "\n", student_id);

	print_students_table(s);

}

void binary_search()
{
	long long student_id;

	printf("请输入学生学号: ");
	scanf("%lld", &student_id);

	Student* s = binary_search_student(student_id);

	if (!s) {
		printf(COLOR_RED "未找到学生" COLOR_RESET);
		return;
	}

	printf(COLOR_GREEN " 找到学号 %lld 的学生" COLOR_RESET "\n", student_id);

	print_students_table(s);
}

void print_all_student()
{
	if (student_count == 0)
	{
		printf(COLOR_YELLOW "学生未存储\n" COLOR_RESET);
	}
	else
	{
		printf("\n所有学生信息如下:\n");
		for (int i = 0; i < student_count; i++)
		{
			printf("学号:%lld  班级:%s  姓名:%s\n", students[i].id, students[i].class_name, students[i].name);
		}
	}
}

void handle_import_scores() {
	import_students();
}

void handle_export_scores() {
	export_students();
}

void initialize_all() {
	// 1. 清空学生列表
	student_count = 0;

	// 2. 释放任务队列
	if (task_queue != NULL) {
		free_task_queue(task_queue);
		task_queue = NULL;
	}

	// 3. 释放学生成绩链表
	for (int i = 0; i < MAX_STUDENTS; ++i) {
		if (students[i].scores != NULL) {
			free_score_list(students[i].scores);
			students[i].scores = NULL;
		}
	}

	printf(COLOR_GREEN "所有数据已初始化,数据结构已清空.\n" COLOR_RESET);
}

void print_score_table_formatted()
{
	print_all_students_score_formatted();
}

batch_ops.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#pragma once
#ifndef BATCH_OPS_H
#define BATCH_OPS_H

#include <stdbool.h>

#define MAX_COURSE_NAME_LENGTH 100
#define MAX_CLASS_NAME_LENGTH 100
#define MAX_NAME_LENGTH 100
#define MAX_COURSE_COUNT 100

void import_students();
void export_students();

#endif // BATCH_OPS_H

batch_ops.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#include "batch_ops.h"
#include "student.h"
#include "score.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "color.h"

void import_students() {
    //char filename[100];
    //printf("请输入CSV文件名(例如:scores.csv):");

    //while (1) {
    //    if (fgets(filename, sizeof(filename), stdin) == NULL) {
    //        printf("输入错误,请重试。\n");
    //        continue;
    //    }

    //    // 移除换行符
    //    filename[strcspn(filename, "\n")] = 0;

    //    // 检查输入是否为空
    //    if (strlen(filename) == 0) {
    //        printf("请输入有效的文件名!\n");
    //        continue;
    //    }

    //    break;
    //}
    char filename[100];

    int c;
    scanf("%*[^\n]");  // 忽略当前行未读取的部分
    scanf("%*c");      // 忽略换行符

    printf("请输入CSV文件名(例如:scores.csv):");
    fgets(filename, 100, stdin);
    filename[strcspn(filename, "\n")] = 0; // 移除换行符

    // 检查文件名是否为空
    if (strlen(filename) == 0) {
        printf(COLOR_YELLOW "请输入有效的文件名!" COLOR_RESET "\n");
        return;
    }

    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        printf(COLOR_RED "无法打开文件 %s" COLOR_RESET "\n", filename);
        return;
    }

    char line[1024];

    // 读取表头行
    if (fgets(line, sizeof(line), file) == NULL) {
        printf(COLOR_RED "文件为空或格式不正确" COLOR_RESET "\n");

        fclose(file);
        return;
    }

    // 解析表头,提取课程名称
    char* header = strdup(line);
    char* token = strtok(header, ",");
    if (token == NULL || strcmp(token, "学号") != 0) {
        printf(COLOR_RED "未找到学号列" COLOR_RESET "\n");
        free(header);
        fclose(file);
        return;
    }

    token = strtok(NULL, ",");
    if (token == NULL || strcmp(token, "班级") != 0) {
        printf(COLOR_RED "未找到班级列" COLOR_RESET "\n");
        free(header);
        fclose(file);
        return;
    }

    token = strtok(NULL, ",");
    if (token == NULL || strcmp(token, "姓名") != 0) {
        printf(COLOR_RED "未找到姓名列" COLOR_RESET "\n");
        free(header);
        fclose(file);
        return;
    }

    token = strtok(NULL, ",");
    if (token == NULL || strcmp(token, "性别") != 0) {
        printf(COLOR_RED "未找到性别列" COLOR_RESET "\n");
        free(header);
        fclose(file);
        return;
    }

    // 存储课程名称
    char courses[20][MAX_COURSE_NAME_LENGTH];
    int courseCount = 0;
    while ((token = strtok(NULL, ",")) != NULL) {
        if (courseCount < 20) {
            strncpy(courses[courseCount], token, MAX_COURSE_NAME_LENGTH - 1);
            courses[courseCount][MAX_COURSE_NAME_LENGTH - 1] = '\0';
            courseCount++;
        }
    }
    free(header);

    // 逐行读取学生数据
    int importedCount = 0;
    int errorCount = 0;

    while (fgets(line, sizeof(line), file)) {
        line[strcspn(line, "\n")] = 0; // 移除换行符

        // 解析学生数据
        char* data = strdup(line);
        token = strtok(data, ",");
        if (token == NULL) {
            free(data);
            continue;
        }

        // 提取学号
        long long id = atoll(token);  //改为 long long
        if (id <= 0) {
            printf(COLOR_YELLOW "无效的学号 %s,跳过该行" COLOR_RESET "\n" , token);
            free(data);
            errorCount++;
            continue;
        }

        // 提取班级
        token = strtok(NULL, ",");
        char class_name[MAX_CLASS_NAME_LENGTH] = { 0 };
        if (token != NULL) {
            strncpy(class_name, token, MAX_CLASS_NAME_LENGTH - 1);
        }

        // 提取姓名
        token = strtok(NULL, ",");
        char name[MAX_NAME_LENGTH] = { 0 };
        if (token != NULL) {
            strncpy(name, token, MAX_NAME_LENGTH - 1);
        }

        // 提取性别
        token = strtok(NULL, ",");
        char gender[10] = { 0 };
        if (token != NULL) {
            strncpy(gender, token, 9);
        }

        // 检查学生是否存在,不存在则添加
        Student* s = find_student(id);  // 使用 find_student 替代 findStudentIndex
        if (!s) {
            if (!add_student(id, name, gender, class_name)) {  // 使用 class_name 参数
                printf(COLOR_RED "添加学生 %s (ID: %lld) 失败" COLOR_RESET "\n", name, id);
                free(data);
                errorCount++;
                continue;
            }
            s = find_student(id);  //重新获取学生指针
        }

        // 提取课程成绩
        for (int i = 0; i < courseCount; i++) {
            token = strtok(NULL, ",");
            if (token == NULL) {
                printf(COLOR_YELLOW "学生 %s (ID: %lld) 缺少课程成绩,跳过该课程" COLOR_RESET "\n", name, id);

                continue;
            }

            double score = atof(token);  // 保留两位小数
            if (score < 0 || score > 100) {
                printf(COLOR_YELLOW "学生 %s (ID: %lld) 的 %s 成绩 %s 无效,设为 0.00" COLOR_RESET "\n", name, id, courses[i], token);
                score = 0.00;
            }

            // 添加成绩
            s->scores = add_score(s->scores, courses[i], score);
        }

        importedCount++;
        free(data);
    }

    fclose(file);
    printf(COLOR_GREEN "导入完成!成功导入 %d 名学生,%d 个错误\n" COLOR_RESET, importedCount, errorCount);
}


// 比较函数:按学号升序排序
int compare_students(const void* a, const void* b) {
    Student* s1 = (Student*)a;
    Student* s2 = (Student*)b;
    return (s1->id > s2->id) - (s1->id < s2->id); // 返回正数表示 s1 > s2
}

// 批量导出成绩到CSV文件
void export_students() {
    char filename[100];

    int c;
    scanf("%*[^\n]");  // 忽略当前行未读取的部分
    scanf("%*c");      // 忽略换行符

    printf("请输入CSV文件名(例如:exported_scores.csv):");
    fgets(filename, sizeof(filename), stdin);
    filename[strcspn(filename, "\n")] = 0;

    FILE* file = fopen(filename, "w");
    if (!file) {
        printf(COLOR_RED "无法打开文件 %s" COLOR_RESET "\n", filename);
        return;
    }

    // 1. 按学号排序学生
    qsort(students, student_count, sizeof(Student), compare_students);

    // 2. 收集所有课程名称
    char courses[MAX_COURSE_COUNT][MAX_COURSE_NAME_LENGTH];
    int course_count = 0;

    for (int i = 0; i < student_count; ++i) {
        ScoreNode* node = students[i].scores;
        while (node) {
            int exists = 0;
            for (int j = 0; j < course_count; ++j) {
                if (strcmp(node->course, courses[j]) == 0) {
                    exists = 1;
                    break;
                }
            }
            if (!exists && course_count < MAX_COURSE_COUNT) {
                strcpy(courses[course_count++], node->course);
            }
            node = node->next;
        }
    }

    // 倒序 courses[],还原成与 CSV 表头一致的“高数, 英语”顺序
    for (int i = 0; i < course_count / 2; ++i) {
        char tmp[MAX_COURSE_NAME_LENGTH];
        strcpy(tmp, courses[i]);
        strcpy(courses[i], courses[course_count - 1 - i]);
        strcpy(courses[course_count - 1 - i], tmp);
    }

    // 3. 写入表头
    fprintf(file, "学号,班级,姓名,性别");
    for (int i = 0; i < course_count; ++i) {
        fprintf(file, ",%s", courses[i]);
    }

    // 4. 写入学生数据
    for (int i = 0; i < student_count; ++i) {
        Student* s = &students[i];
        fprintf(file, "%lld,%s,%s,%s", s->id, s->class_name, s->name, s->gender);

        // 按课程顺序写入成绩
        for (int j = 0; j < course_count; ++j) {
            double grade = 0.0;
            ScoreNode* node = s->scores;
            while (node) {
                if (strcmp(node->course, courses[j]) == 0) {
                    grade = node->grade;
                    break;
                }
                node = node->next;
            }
            fprintf(file, ",%.2f", grade);
        }
        fprintf(file, "\n");
    }

    fclose(file);
    printf(COLOR_GREEN "成功导出 %d 名学生到文件 %s\n" COLOR_RESET, student_count, filename);
}

print_util.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include "print_util.h"
#include "score.h"
#include "color.h"

#define MAX_COURSE_COUNT 100
#define MAX_NAME_LEN     64

void print_students_table(const Student* s) {
    if (!s) return;

    // 1. 收集课程信息
    char courses[MAX_COURSE_COUNT][MAX_NAME_LEN] = { {0} };
    int course_count = 0;
    for (ScoreNode* n = s->scores; n; n = n->next) {
        strncpy(courses[course_count++], n->course, MAX_NAME_LEN);
    }

    // 2. 计算列宽
    int w_id = (int)strlen("学号");
    int w_cls = (int)strlen("班级");
    int w_name = (int)strlen("姓名");
    int w_gender = (int)strlen("性别");
    int* w_course = malloc(course_count * sizeof(int));

    char buf[64];
    int len = snprintf(buf, sizeof(buf), "%lld", s->id);
    if (len > w_id) w_id = len;
    len = (int)strlen(s->class_name);
    if (len > w_cls) w_cls = len;
    len = (int)strlen(s->name);
    if (len > w_name) w_name = len;
    len = (int)strlen(s->gender);
    if (len > w_gender) w_gender = len;

    for (int i = 0; i < course_count; ++i) {
        len = (int)strlen(courses[i]);
        if (len < 5) len = 5; // 最小列宽保证对齐
        w_course[i] = len;
    }

    // 3. 打印表头
    printf("\033[1;32m");  // 绿色加粗
    printf("%-*s | %-*s | %-*s | %-*s",
        w_id, "学号",
        w_cls, "班级",
        w_name, "姓名",
        w_gender, "性别");
    for (int i = 0; i < course_count; ++i) {
        printf(" | %-*s", w_course[i], courses[i]);
    }
    printf("\033[0m\n");

    // 分隔线
    int total = w_id + w_cls + w_name + w_gender + 3 * 4;
    for (int i = 0; i < course_count; ++i)
        total += w_course[i] + 3;
    for (int i = 0; i < total; ++i) putchar('-');
    putchar('\n');

    // 4. 打印数据
    printf("\033[1;36m");  // 青色加粗
    printf("%-*lld | %-*s | %-*s | %-*s",
        w_id, s->id,
        w_cls, s->class_name,
        w_name, s->name,
        w_gender, s->gender);
    for (int i = 0; i < course_count; ++i) {
        double score = 0;
        for (ScoreNode* n = s->scores; n; n = n->next) {
            if (strcmp(n->course, courses[i]) == 0) {
                score = n->grade;
                break;
            }
        }
        printf(" | %-*.2f", w_course[i], score);
    }
    printf("\033[0m\n");

    free(w_course);
}

// --- 所有学生成绩输出 ---
void print_all_students_score_formatted(void) {
    extern Student students[];
    extern int student_count;

    if (student_count == 0) {
        puts("没有学生数据!");
        return;
    }

    // 1. 统一收集所有课程名(去重 + 保顺序)
    char courses[MAX_COURSE_COUNT][MAX_NAME_LEN] = { {0} };
    int course_count = 0;
    for (int i = 0; i < student_count; ++i) {
        for (ScoreNode* n = students[i].scores; n; n = n->next) {
            int found = 0;
            for (int j = 0; j < course_count; ++j)
                if (strcmp(courses[j], n->course) == 0) { found = 1; break; }
            if (!found)
                strncpy(courses[course_count++], n->course, MAX_NAME_LEN);
        }
    }

    // 2. 计算列宽
    int w_id = strlen("学号"), w_cls = strlen("班级"), w_name = strlen("姓名"), w_gender = strlen("性别");
    int* w_course = malloc(course_count * sizeof(int));
    for (int i = 0; i < course_count; ++i)
        w_course[i] = strlen(courses[i]);

    char buf[64];
    for (int i = 0; i < student_count; ++i) {
        Student* s = &students[i];
        int len = snprintf(buf, sizeof(buf), "%lld", s->id); if (len > w_id) w_id = len;
        len = strlen(s->class_name); if (len > w_cls) w_cls = len;
        len = strlen(s->name);       if (len > w_name) w_name = len;
        len = strlen(s->gender);     if (len > w_gender) w_gender = len;

        for (int j = 0; j < course_count; ++j) {
            double grade = 0;
            for (ScoreNode* n = s->scores; n; n = n->next)
                if (strcmp(n->course, courses[j]) == 0) { grade = n->grade; break; }
            len = snprintf(buf, sizeof(buf), "%.2f", grade);
            if (len > w_course[j]) w_course[j] = len;
        }
    }

    // 3. 打印表头
    printf("\033[1;32m");
    printf("%-*s | %-*s | %-*s | %-*s",
        w_id, "学号", w_cls, "班级", w_name, "姓名", w_gender, "性别");
    for (int i = 0; i < course_count; ++i)
        printf(" | %-*s", w_course[i], courses[i]);
    printf("\033[0m\n");

    // 分隔线
    int total = w_id + w_cls + w_name + w_gender + 3 * 4;
    for (int i = 0; i < course_count; ++i)
        total += w_course[i] + 3;
    for (int i = 0; i < total; ++i) putchar('-');
    putchar('\n');

    // 4. 打印每个学生
    for (int i = 0; i < student_count; ++i) {
        Student* s = &students[i];
        printf("\033[1;36m");
        printf("%-*lld | %-*s | %-*s | %-*s",
            w_id, s->id, w_cls, s->class_name, w_name, s->name, w_gender, s->gender);
        for (int j = 0; j < course_count; ++j) {
            double grade = 0;
            for (ScoreNode* n = s->scores; n; n = n->next)
                if (strcmp(n->course, courses[j]) == 0) { grade = n->grade; break; }
            printf(" | %-*.2f", w_course[j], grade);
        }
        printf("\033[0m\n");
    }

    free(w_course);
}

print_util.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#ifndef PRINT_NTIL_H
#define PRINT_NTIL_H

#include "student.h"
#include "score.h"

void print_student_score_formatted(const Student* s);
void print_all_students_score_formatted(void);

#endif

color.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#pragma once
#ifndef COLOR_H
#define COLOR_H

#define COLOR_RED     "\033[1;31m"
#define COLOR_GREEN   "\033[1;32m"
#define COLOR_YELLOW  "\033[1;33m"
#define COLOR_BLUE    "\033[1;34m"
#define COLOR_RESET   "\033[0m"

#endif