前言:
作为我的第一篇”大作“哈哈,在此选择了“学生成绩管理系统”作为题目。具体题目的要求找不到了,但菜单第一页显示绝对是足够的。
该程序基于链表实现,以二进制文件储存数据。下面直接看代码: //基于VS2022
代码部分:
菜单:
void menu() //打印菜单;
{
printf_s("///\n");
printf_s("/ 欢迎试用学生成绩管理系统!:) /\n");
printf_s("/ 请选择有关操作(输入相应数字): /\n");
printf_s("// *****\n");
printf_s("// 1.录入学生成绩及有关信息; *****\n");
printf_s("// 2.浏览所有学生成绩; *****\n");
printf_s("// 3.查询某位学生成绩; *****\n");
printf_s("// 4.成绩排序与统计; *****\n");
printf_s("// 5.修改学生信息; *****\n");
printf_s("// 0.退出系统; *****\n");
printf_s("***************************************************\n");
}
首当其冲的便是菜单函数,用于提示按下相应的按键以执行对应操作。下面将逐个实现。
分块程序://依据程序运行方式排序
void Clear()
{
system("pause"); //暂停程序;
system("cls"); //清空控制台,使界面更好看;
}
一个蛮好用的小插件。
void LoadStu(Node*head)
{
FILE* file = fopen("./StuT", "r");
if (!file)
{
printf_s("未找到文件,跳过读取。\n");
return;
}
//创建一个结点,将文件存入内存空间;
Node* new = malloc(sizeof(Node));
new->next = NULL;
Node* move = head;
while (fread(&new->student, sizeof(Stu), 1, file) == 1)
{
move->next = new;
move = new;
new = malloc(sizeof(Node));
new->next = NULL;
}
free(new); //清理多创建的空间;
//关闭文件;
fclose(file);
printf("读取成功。\n");
}
一个文件读取模块,作用于选择操作之前,用于读取已存储文件,涉及文件操作函数。
void InStu(Node*head)
{
Node* new = malloc(sizeof(Node)); //为新的结点分配区域;
new->next = NULL; //初始化;
printf_s("请输入学生的学号、姓名、语文成绩1、数学成绩2、英语成绩3:\n");
scanf("%d%s%d%d%d",&new->student.StuNum, &new->student.Name, & new->student.Score1, & new->student.Score2, & new->student.Score3);
//使用scanf_s需要限制输入字数,不好用;scanf输入时需要以回车键为完成一次输入的标志;
Node* move = head; //创建新指针,使之随列表创建移动,便于操作;
while (move->next != NULL)
{
move = move->next;
}
move->next = new; //将新学生信息插入尾部;
SaveStu(head);
Clear();
}
此程序用于录入学生信息,采用链表的操作将程序插入尾部。
void SaveStu(Node* head)
{
//打开文件;
FILE* file = fopen("./StuT", "w");
if (file == NULL)
{
printf_s("打开文件失败。\n");
return;
}
Node* move = head->next;
while (move != NULL) //写入数据;//结构体;
{
if (fwrite(&move->student, sizeof(Stu), 1, file) != 1)
{
printf_s("保存%s出现错误。\n", move->student.Name);
}
move = move->next;
}
//关闭文件;
fclose(file);
}
运行文件操作,储存链表数据,每一次储存都会覆盖掉上一次的数据。
void PrintStu(Node*head) //遍历链表并打印;
{
Node* move = head->next; //指向首结点(数据结点);//只可用于普通的遍历中;
while (move != NULL)
{
printf_s("学号:%d;\t姓名:%s;\t\n语文成绩1:%d;\t数学成绩2:%d;\t英语成绩3:%d;\t\n", move->student.StuNum, move->student.Name, move->student.Score1, move->student.Score2, move->student.Score3);
move = move->next;
}
}
用于浏览所有学生信息。
void CountStudent(Node* head) //统计学生总数;
{
int count = 0;
Node* move = head->next; //指向首结点(数据结点);//只可用于普通的遍历中;
while (move != NULL)
{
count++;
move = move->next;
}
printf_s("学生总人数为:%d\n", count);
Clear();
}
通过链表计算学生总数。
void FindStuNum(Node*head) //遍历;
{
int StuNum = 0;
printf_s("请输入学生学号:\n");
scanf("%d", &StuNum);
Node* move = head->next;
while (move != NULL)
{
if (StuNum == move->student.StuNum) //找到了;
{
printf_s("学号:%d;\t姓名:%s;\t\n语文成绩1:%d;\t数学成绩2:%d;\t英语成绩3:%d;\t\n", move->student.StuNum, move->student.Name, move->student.Score1, move->student.Score2, move->student.Score3);
Clear();
return; //直接跳出函数;break只跳出循环;
}
move = move->next;
}
printf_s("未找到学生信息。\n");
Clear();
}
依据学号查找学生信息。
void FindName(Node* head)
{
char name[10];
printf_s("请输入学生姓名:\n");
scanf("%s", &name);
Node* move = head->next;
while (move != NULL)
{
if (strcmp(name,move->student.Name)==0) //找到了;strcmp(arr1, arr2)
{
printf_s("学号:%d;\t姓名:%s;\t\n语文成绩1:%d;\t数学成绩2:%d;\t英语成绩3:%d;\t\n", move->student.StuNum, move->student.Name, move->student.Score1, move->student.Score2, move->student.Score3);
Clear();
return; //直接跳出函数;break只跳出循环;
}
move = move->next;
}
printf_s("未找到学生信息。\n");
Clear();
}
依据姓名查找。
void SortStu(Node* head) //通过一个新的指针来控制遍历的次数;//Node*turn;
{
int count = 0; //示例:统计某科目不及格人数;
Node* save = NULL;
Node* move = NULL;
printf_s("// 选择需要排序的科目:1.语文;2.数学;3.英语;\n");
char m = _getch();
switch (m)
{
case '1':
for (Node* turn = head->next; turn->next != NULL; turn = turn->next) //科目1;
{
for (move = head->next; move->next != save; move = move->next) //从前到后,从小到大排序;
{
if (move->student.Score1 > move->next->student.Score1)
{
Stu temp = move->student;
move->student = move->next->student;
move->next->student = temp;
}
}
save = move; //保存最大的;
}
for (move = head->next; move->student.Score1 <= 60; move = move->next)
{
count++;
}
printf_s("// 科目1不及格的学生人数为:%d\n", count);
break;
case '2':
for (Node* turn = head->next; turn->next != NULL; turn = turn->next) //科目2;
{
for (move = head->next; move->next != save; move = move->next) //从前到后,从小到大排序;
{
if (move->student.Score2 > move->next->student.Score2)
{
Stu temp = move->student;
move->student = move->next->student;
move->next->student = temp;
}
}
save = move; //保存最大的;
}
for (move = head->next; move->student.Score2 <= 60; move = move->next)
{
count++;
}
printf_s("// 科目2不及格的学生人数为:%d\n", count);
break;
case '3':
for (Node* turn = head->next; turn->next != NULL; turn = turn->next) //科目3;
{
for (move = head->next; move->next != save; move = move->next) //从前到后,从小到大排序;
{
if (move->student.Score3 > move->next->student.Score3)
{
Stu temp = move->student;
move->student = move->next->student;
move->next->student = temp;
}
}
save = move; //保存最大的;
}
for (move = head->next; move->student.Score3 <= 60; move = move->next)
{
count++;
}
printf_s("// 科目3不及格的学生人数为:%d\n", count);
break;
default:
printf_s("// 选择出现错误。\n");
}
PrintStu(head);
Clear();
}
计算不及格人数,这里手动输入了三门科目,但如果用更好的结构体操作可以做到更多科目,我想应该会有大佬能加上Excel,但并不是此文章的重点所在。
void ModifyStu(Node* head)
{
printf_s("// 请输入要修改的学生学号:\n");
int StuNum;
scanf("%d", &StuNum);
Node* move = head->next;
while (move != NULL)
{
if (move->student.StuNum == StuNum)
{
printf_s("请输入学生的姓名、成绩1、成绩2、成绩3:\n");
scanf("%s%d%d%d", &move->student.Name, &move->student.Score1,&move->student.Score2, &move->student.Score3);
SaveStu(head); //保存信息;
printf_s("// 修改成功。\n");
Clear();
return;
}
move = move->next;
}
printf_s("// 未找到学生信息。\n");
Clear();
}
依据学号修改学生信息。
void DelStu(Node* head)
{
printf_s("// 请输入要删除学生的学号:\n");
int StuNum;
scanf("%d", &StuNum);
Node* move = head;
while (move->next != NULL) //更改其有关信息的前一个结点
{
if (move->next->student.StuNum == StuNum)
{
Node* p = move->next;
move->next = move->next->next; //跳过结点;
free(p);
p = NULL;
SaveStu(head);
printf_s("// 删除成功。\n");
Clear();
return;
}
move = move->next;
}
printf_s("// 未找到学生。\n");
Clear();
}
依据学号删除学生信息。
int main()
{
//创建头结点;
Node* head = malloc(sizeof(Node)); //头结点的地址;
head->next = NULL; //空的头结点方便后续操作;
LoadStu(head);
char m; //通用选择数;
while (1)
{
menu(); //打印菜单;
char Chi = _getch(); //避免scanf产生的一些错误;
switch (Chi)
{
case '1': //录入学生成绩及有关信息;
InStu(head);
break;
case '2': //浏览所有学生成绩;
PrintStu(head);
CountStudent(head); //计算学生人数;
break;
case '3': //查询某位学生成绩;//学号、姓名;
printf_s("// 请选择查找方式:1.学号查找; 2.姓名查找; //\n");
m = _getch();
switch (m)
{
case '1':
FindStuNum(head);
break;
case '2':
FindName(head);
break;
default:
printf_s("选择出错了!请重新输入。\n");
Clear();
break;
}
break;
case '4': //成绩排序与统计;
SortStu(head);
break;
case '5': //删除学生信息; //+修改信息;
printf_s("// 请选择更改方式:1.修改学生信息; 2.删除学生信息; //\n");
m = _getch();
switch (m)
{
case '1':
ModifyStu(head);
break;
case '2':
DelStu(head);
break;
default:
printf_s("选择出错了!请重新输入。\n");
Clear();
break;
}
break;
case '0': //退出系统;
system("cls");
exit(0);
break;
default: //意外......
printf_s("选择出错了!请重新输入。\n");
Clear();
break;
}
}
return 0;
}
主函数。
#include <stdio.h>
#include <stdlib.h> //为后续使用链表做准备;
#include <conio.h>
#include <string.h>
//学生信息;
typedef struct _Student //使用typedef对struct _Student重命名
{
int StuNum;
char Name[10];
// char Course1[5]; //设定固定科目;注释3行;
int Score1;
// char Course2[5];
int Score2;
// char Course3[5];
int Score3;
}Stu;
//结点信息; //链表结点;
typedef struct _Node
{
Stu student;
struct _Node* next;
}Node;
//有关函数声明;
void menu(); //打印菜单选项;
void Clear(); //暂停和清理;
void LoadStu(Node* head); //程序启动时先读取文件;
void InStu(Node* head); //录入学生信息;
void SaveStu(Node* head); //存储学生信息;
void PrintStu(Node* head); //打印ALL有关信息;
void CountStudent(Node* head); //统计学生总人数;
void FindStuNum(Node* head); //学号查找;
void FindName(Node* head); //姓名查找;
void SortStu(Node* head); //成绩排序与统计;
void DelStu(Node* head); //删除学生信息;
void ModifyStu(Node* head); //修改学生信息;
头文件。
一点解析:
在此程序中,先定义一个头结点,始终指向链表的第一个空的头结点,链表各数据之间通过结点相连接,通过结点可直接访问链表中的数据。故再定义一个Node*move指针用来实现对链表的操作。设计结构如下:
有关文件操作函数的一些内容:
fopen:
"r" 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。
"w" 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a" 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
"r+"以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。
"w+"以“写入/更新”方式打开文件,相当于w和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;
如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a+"以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;
如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
"t" 文本文件。如果不写,默认为"t"。
"b" 二进制文件。