C - 链表和文件操作实现「学生信息管理系统」

Dec 24, 2016

该管理系统可以实现学生信息的读取、录入、删除、修改、查询、打印、保存。更多的功能大家可以自己拓展(比如排序,按照分数段查询)

注:笔者的 C 语言水暂时位于大一的水平,代码免不了种种问题,望大牛轻喷+指正。

实现学生信息的读取、录入、删除、修改、查询、打印、保存。
读取:从文件中读取已有内容;
录入:录入初始的学生信息数据;
输出:将学生信息数据输出到屏幕;
删除:用户指定学生学号,将该数据删除;
修改:用户指定待修改数据的学生学号,录入修改后的数据;
保存:保存学生信息到文件。
学生信息包括:学号、姓名、性别、班级、5门课程的成绩。
文件的第一行记录学生的个数。
文件其他行,每一行为一个学生的信息。

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
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

char filename[20];

typedef struct student{
int num;
char name[20];
char sex[4];
int classNo;
float math;
float English;
float Virtue; //思修
float code;
float other;
struct student *next;
}STD;

STD *create(int *num);
void input(STD *head,int *num);
void add(STD *head,int *num);
void del(STD *head,int *num);
void change(STD *head);
void search(STD *head);
void print(STD *head,int *num);
void save(STD *head,int *num);

void menu();

int main()
{
int choose = 0;
int count = 0;
STD *head;
FILE *fp;
printf("请输入文件所在位置:\n");
scanf("%s",filename);
fp = fopen(filename,"r");
if (fp == NULL)
{
printf("文件打开失败,程序结束\n");
return 0;
}
fclose(fp);
head = create(&count);
if (head == NULL)
{
return 0;
}
printf("当前有%d条学生信息.\n",count);
while(count == 0)
{
printf("您需要输入学生信息后才能继续操作:\n");
input(head,&count);
}
menu();
scanf("%d",&choose);
while(1){
switch(choose){
case 1:
add(head,&count);
break;
case 2:
del(head,&count);
break;
case 3:
search(head);
break;
case 4:
change(head);
break;
case 5:
print(head,&count);
break;
case 6:
save(head,&count);
break;
case 0:
save(head,&count);
return 0;
break;
default:
break;
}
menu();
scanf("%d",&choose);
}
return 0;
}

STD *create(int *num)
{
STD *head,*p,*next,*tmp; //tmp代表前一个
FILE *fp = fopen(filename,"rb");
head = (STD *)malloc(sizeof(STD));
if (head == NULL)
{
printf("内存分配失败,程序结束\n");
return NULL;
}
head->next = NULL;
p = head;
fread(num,sizeof(int),1,fp);
if(*num == 0){
return head;
}
while(!feof(fp)){
next = (STD *)malloc(sizeof(STD));
if (next == NULL)
{
printf("内存分配失败,程序结束\n");
return NULL;
}
fread(next,sizeof(STD),1,fp);
p->next = next;
tmp = p;
p = next;
}
free(p);
tmp->next = NULL;
return head;


}
void input(STD *head,int *num)
{
//首次输入信息
STD *p,*next;
int times = 1;
p = head;
while(times){
next = (STD *)malloc(sizeof(STD));
if (next == NULL)
{
printf("内存分配失败,程序结束\n");
return ;
}
printf("请输入学生的 学号 姓名 性别 班级 高数 英语 思修 程序设计 其他,用空格隔开(当学号为0时输入结束)\n");
scanf("%d",&next->num);
if (next->num != 0)
{
scanf("%s%s%d%f%f%f%f%f",next->name,next->sex,&next->classNo,&next->math,&next->English,&next->Virtue,&next->code,&next->other);
(*num)++;
p->next = next;
p = next;
}else{
times = 0;
}
}
p->next = NULL;
if (*num != 0)
{
printf("录入成功,欢迎继续使用!\n");
}

}

void add(STD *head,int *num)
{
STD *p,*next;
int i = 0;
int position;
p = head;
printf("当前已经有%d条学生信息\n",*num);
printf("请输入要增加到的位置: \n");
scanf("%d",&position);
while(p != NULL){
if (position - 1 == i)
{
next = (STD *)malloc(sizeof(STD));
if (next == NULL)
{
printf("内存分配失败,程序结束\n");
return ;
}
printf("请输入要增加的学生的 学号 姓名 性别 班级 高数 英语 思修 程序设计 其他,用空格隔开(当学号为0时输入结束)\n");
scanf("%d%s%s%d%f%f%f%f%f",&next->num,next->name,next->sex,&next->classNo,&next->math,&next->English,&next->Virtue,&next->code,&next->other);
(*num)++;
next->next = p->next;
p->next = next;
printf("增加成功\n");
return ;
}else{
i++;
p = p->next;
}
}
printf("增加失败\n");
}
void del(STD *head,int *num)
{
int s_num;
char s[2];
STD *p,*tmp;
p = head;
tmp = NULL;
printf("请输入要删除学生的学号:\n");
scanf("%d",&s_num);
while(p != NULL){
if (p->num == s_num)
{
printf("已经找到学号为%d %s 同学的信息,是否删除:(y/n)\n",p->num,p->name);
scanf("%s",s);
if (strcmp(s,"Y") == 0||strcmp(s,"y") == 0)
{
tmp->next = p->next;
free(p);
(*num)--;
printf("删除成功\n");
return ;
}
}else{
tmp = p;
p = p->next;
}
}
printf("删除失败\n");

}
void change(STD *head)
{
int s_num;
char s[2];
STD *p;
p = head;
printf("请输入要修改学生的学号: \n");
scanf("%d",&s_num);
while(p != NULL){
if (p->num == s_num)
{
printf("已经找到学号为 %d|%s同学的信息,是否修改(y/n):\n",p->num,p->name);
scanf("%s",s);
if (strcmp(s,"Y") == 0||strcmp(s,"y") == 0)
{
printf("请输入该生新的 学号 姓名 性别 班级 高数 英语 思修 程序设计 其他,用空格隔开:\n");
scanf("%d%s%s%d%f%f%f%f%f",&p->num,p->name,p->sex,&p->classNo,&p->math,&p->English,&p->Virtue,&p->code,&p->other);
printf("修改成功\n");
return ;
}
}else{
p = p->next;
}
}
printf("修改失败\n");
}
void search(STD *head)
{
int s_num;
STD *p;
p = head;
printf("请输入要查询的学生的学号: \n");
scanf("%d",&s_num);
while(p != NULL){
if (p->num == s_num)
{
printf("已找到该生信息: \n");
printf("学号: %d | 姓名: %s | 性别: %s | 班级: %d | 高数: %.2f | 英语: %.2f | 思修: %.2f | 程序设计: %.2f | 其他: %.2f |\n",p->num,p->name,p->sex,p->classNo,p->math,p->English,p->Virtue,p->code,p->other);;
return ;
}else{
p = p->next;
}
}
printf("未找到信息,请检查信息输入是否正确\n");
}
void print(STD *head,int *num)
{
STD *p;
p = head->next;
printf("/*********************************/\n");
printf("学号 姓名 性别 班级 高数 英语 思修 程序设计 其他\n");
while(p != NULL){
printf("%-10d%10s%4s%4d%6.2f%6.2f%6.2f%6.2f%6.2f\n",p->num,p->name,p->sex,p->classNo,p->math,p->English,p->Virtue,p->code,p->other);
p = p->next;
}
printf("/*********************************/\n");
printf("\t\t当前系统有 %d 位同学的信息\n",*num);
}
void save(STD *head,int *num)
{
STD *p;
FILE *fp;
int i = 0;
p = head->next;
if ((fp = fopen(filename,"wb")) == 0)
{
printf("文件打开失败,请检查文件路径是否正确\n");
printf("当前文件路径为:%s\n",filename);
return ;
}
fwrite(num,sizeof(int),1,fp);
while(p != NULL){
if (fwrite(p,sizeof(STD),1,fp) != 0)
{
i++;
}
p = p->next;
}
if (i == *num)
{
printf("数据保存成功!\n");
}else{
printf("共有%d条数据,其中已保存%d条数据,%d条保存失败!\n",*num,i,*num-i);
}
}
void menu()
{
printf("||*欢迎使用本学生管理系统*||\n");
printf("1.增加学生信息\n");
printf("2.删除学生信息\n");
printf("3.查找学生信息\n");
printf("4.修改学生信息\n");
printf("5.查看全部信息\n");
printf("6.保存所有更改\n");
printf("0.保存并退出\n");
printf("||********************||\n");
printf("请输入对应功能前的序号: \n");
}

在写代码的时候,笔者遇到了一个问题,看上述代码的朋友可能会注意到我在 create 函数中用到了一个结构体指针 tmp,可能有的朋友会不懂,这里 tmp 代表当前链节的上一个链节。

当文件内部的位置指针指向文件结束时,并不会立即设置FILE结构中的文件结束标识,只有再执行一次读文件操作,才会设置结束标志,此后调用feof()才会返回为真。

通俗一点说,在正常使用 feof 时往往会出现多读取一行的问题,(在第一版程序中 如果读取一个 有3个学生信息的二进制文件时,会显示4行学生信息,其中第四个同学的所有信息都是0),这样就会比较尴尬。

通过修改,在读取二进制文件后,当读取到文件结尾时,会创建一个链节,且内容位空,此时我们在上个循环中获取倒数第二个链节(即最后一个有意义的链节),让倒数第二个链节的 next = NULL,然后释放掉最后一个无意义的链节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while(!feof(fp)){
next = (STD *)malloc(sizeof(STD));
if (next == NULL)
{
printf("内存分配失败,程序结束\n");
return NULL;
}
fread(next,sizeof(STD),1,fp);
p->next = next;
tmp = p;
p = next;
}
free(p);
tmp->next = NULL;