前言
本管理系统是一个简易版本的学生管理系统,中间仍存在许多的问题,笔者目前展示的是未完全完成状态。后面增加功能后再进行更改。
这是本管理系统的大体逻辑:
一、文件的读取、写入和保存
结构体变量
typedef struct student
{
char name[20];//姓名
char num[20];//学号
char iclass[10];//班级
int chinese;//语文
int math;//数学
int english;//英语
int sum;//总分
char key[20];//密码
}stu;
typedef struct node
{
struct student stu;
struct node* next;
}node;
这里先给出我的结构体变量,由于教师和学生大致逻辑相同,这里就不多做展示了。
1.1文件的读取和写入
void ReadStudent()
{
char file[100] = { "class.txt" };
FILE* fp = fopen(file, "r");
node* move = head/*>next*/;
if (fp == NULL)
{
fp = fopen(file, "w+");//如果该文件不存在,则将该文件创建出来。
fclose(fp);
}
else
{
while (!feof(fp))
{
node* p = (node*)malloc(sizeof(node));
p->next = NULL;
fscanf(fp, "%s %s %s %d %d %d %s\n", p->stu.name, p->stu.num,
p->stu.iclass, &p->stu.chinese, &p->stu.math, &p->stu.english, p->stu.key);
while (move->next != NULL) {
move = move->next;
}
p->stu.sum = p->stu.chinese + p->stu.math + p->stu.english;
move -> next = p;
}
printf("读取成功\n");
}
fclose(fp);
}
由于总分是三科成绩之和,所以笔者并未将其存入文件中,在每次打开文件时做加法即可。
这里我将所有学生信息存储在一个文件中,直接将所有信息读取至文件中即可。
1.2文件的写入
void SaveInfo()
{
FILE* fp = fopen("class.txt", "w");
if (fp == NULL)
{
printf("文件打开失败!\n");
}
node* p = head->next;
while (p)
{
fprintf(fp, "%s %s %s %d %d %d %s\n", p->stu.name, p->stu.num,
p->stu.iclass, p->stu.chinese, p->stu.math, p->stu.english, p->stu.key);
p = p->next;
}
fclose(fp);
}
每次保存学生信息都将整个链表保存一遍,每遍历一个节点,就用fprintf将该节点保存至文件之中。由于文件仅有一个,笔者并未按班级分开保存,故而信息的保存不用判断,较为简单。
二、 管理系统功能
主界面
2.1登录界面
void LoginBackGround()
{
printf("欢迎来到后台登录界面!");
char a[7] = { "123456" };
char b[7] = { 0 };
for (int j = 3; j > 0; j--)
{
printf("\n密码:");
int i = 0;
while (1)
{
key[i] = getch();
if (key[i] == '\r')
break;
if (key[i] == '\b' && i > 0)
{
printf("\b");
_putch(' ');
printf("\b");
key[--i] = '\0';
continue;
}
if (key[i] == '\b' && i == 0)
{
printf("\b");
_putch(' ');
printf("\b");
i = -1;
continue;
}
printf("*");
i++;
}
key[i] = '\0';
if (strcmp(key, a) == 0)
{
printf("\n您的密码输入正确,即将进入个人界面!\n");
system("pause");
system("cls");
Menu3();
}
else
{
if (j == 1)
break;
printf("您的密码输入有误,请重新输入。");
printf("\n请注意您还有%d次机会", j-1);
}
}
printf("您已经三次输入错误,即将返回主页面");
return;
}
笔者这里隐式输入中只实现了回删并重新输入,并未用ASCII码表限制一些不可用符号的输入,并未完全掌握这里的知识,过段时间学会后再加上。
2.2后台功能的实现
后台界面如图所示
2.2.1增加学生
void AddStudent()
{
printf("注意:请输入正确的班级及学号,不然无法增加学生!\n");
node* move = head;
node* fresh = (node*)malloc(sizeof(node));
fresh->next = NULL;
printf("请输入您要增加学生的姓名:");
scanf("%s", fresh->stu.name);
strcpy(name, fresh->stu.name);
while (move->next != NULL)
{
if (strcmp(move->stu.name, fresh->stu.name) == 0)
{
printf("\n该学生已被系统录入,请勿重新录入!\n");
system("pause");
system("cls");
return;
}
move = move->next;
}
printf("\n请依次输入该学生学号,班级,语文成绩,数学成绩,英语成绩:\n");
scanf("%s %s %d %d %d", fresh->stu.num, fresh->stu.iclass, &fresh->stu.chinese,&fresh->stu.math, &fresh->stu.english);
fresh->stu.sum = fresh->stu.chinese + fresh->stu.math + fresh->stu.english;
if (strcmp(fresh->stu.iclass, "网络1班") == 0 || strcmp(fresh->stu.iclass, "网络2班") == 0 || strcmp(fresh->stu.iclass, "网络3班") == 0)
{
strcpy(fresh->stu.key, "000000");
move->next = fresh;
printf("增加成功!");
return;
}
else {
printf("没有这个班级,无法增加该学生!\n");
printf("即将返回上个页面!\n");
system("pause");
system("cls");
Menu3();
}
}
由于笔者的问题,所有班级学生姓名不能重复,后期在增加判断姓名的功能。
下文中先判断班级是否是存在的班级,如果不存在该班级,也无法录入该学生信息,避免出现录入不存在班级的问题。同时由于教师注册操作和增加学生实现方式相同,在这里就不过多做展示。
2.2.2修改学生信息
void ChangeStudent()
{
node* move = head;
printf("请输入您要更改学生的姓名:");
scanf("%s", name);
while (move->next != NULL)
{
if (strcmp(name, move->stu.name) == 0)
{
printf("已找到该名学生,请输入您要更改的内容:");
printf("1.学号 2.班级 \n3.语文 4.数学 5.英语\n");
int n;// score;
scanf("%d", &n);
int score = 0;
switch(n)
{
case 1:
printf("请输入该名学生新的学号:");
char num[20];
scanf("%s", num);
strcpy(move->stu.num, num);
printf("\n修改成功!\n");
return;
break;
case 2:
printf("请输入该名学生新转入的班级:");
char iclass[20];
scanf("%s", iclass);
strcpy(move->stu.iclass, iclass);
printf("修改成功!");
return;
break;
case 3:
printf("请输入该名学生正确的语文成绩:");
scanf("%d", &score);
move->stu.sum -= move->stu.chinese;
move->stu.chinese = score;
move->stu.sum += move->stu.chinese;
printf("修改成功!");
return;
break;
case 4:
printf("请输入该名学生正确的数学成绩:");
scanf("%d", &score);
move->stu.sum -= move->stu.math;
move->stu.math = score;
move->stu.sum += move->stu.math;
printf("修改成功!");
return;
break;
case 5:
printf("请输入该名学生正确的英语成绩:");
scanf("%d", &score);
move->stu.sum -= move->stu.english;
move->stu.english = score;
move->stu.sum += move->stu.english;
printf("修改成功!");
return;
break;
default:
printf("没有这个选项,请重新输入!\n");
return;
break;
}
}
move = move->next;
}
printf("未找到该名学生,请确您输入的信息!");
}
由于查询学生信息仅仅是将学生查询出后打印该学生信息,查询操作在修改学生信息中有体现,故该功能也不做展示。修改操作即是将想要修改的一项更改,将链表中的东西修改以后,将该函数执行完成后一定要将链表的内的信息即使保存在文件中。即使用一次文件的写入函数。
2.2.3查询年纪成绩
该操作要对成绩进行排序,笔者为了练习链表的排序,在这里用了三种不同的排序方法分别排序了我的三科成绩。由于任一一种排序均可实现对成绩的排序功能,笔者这里只放一种排序进行展示,其余的排序方法不做展示。
void Bubble(node* head)//冒泡排序
{
int i, count = 0, num;
node* p = head;
node* q, *tail;
while (p->next != NULL)
{
count++;
p = p->next;
}
for (int i = 0; i < count - 1; i++)
{
num = count - i - 1;
q = head->next;
p = q->next;
tail = head;
while (num--)
{
if (q->stu.sum < p->stu.sum)
{
q->next = p->next;
p->next = q;
tail->next = p;
}//交换节点
tail = tail->next;
q = tail->next;
p = q->next;
}
}
}
void ListStudent()//打印学生信息
{
node* move = head->next;
printf("姓名\t班级\t\t学号\t语文\t数学\t英语\t总分\n");
while (move != NULL)
{
move->stu.sum = move->stu.chinese + move->stu.math + move->stu.english;
printf("%s\t%s\t\t%s\t%d\t%d\t%d\t%d\n", move->stu.name, move->stu.iclass, move->stu.num,
move->stu.chinese, move->stu.math, move->stu.english, move->stu.sum);
move = move->next;
}
}
由于笔者没有将所有信息专门拿出来放在一个链表中进行排序,直接对原来链表进行排序并更改链表顺序,故而每次排序后链表顺序都会改变。如果想让原有链表输入学生信息顺序不变,请使用一个新的链表放置学生信息,并进行学生排序。
其余几个功能的实现方式都在这三个功能实现是被使用,故笔者在这里不展示其余几个功能的代码,避免浪费空间时间。
2.3学生界面功能实现
2.3.1学生注册
该功能不放在学生界面内,而是主界面。但涉及到学生自己操作,故而放在这里进行讲解。
void LoginStudent()
{
printf("注意:本系统只能本年纪学生注册,其余人无法注册!\n");
printf("注意:初始密码不能当作您的密码使用,否则无法登录!\n");
printf("注意:请输入6-18位之间仅含数字和字母的密码!\n");
node* move = head->next;
printf("请输入您的姓名,学号,班级:");
char iclass[20], num[20];
scanf("%s %s %s", name, num, iclass);
int i = 0;
while (move != NULL)
{
if (strcmp(name, move->stu.name) == 0&& strcmp(num, move->stu.num) == 0&& strcmp(iclass, move->stu.iclass) == 0)
{
printf("您的信息全部正确,请修改您的密码!\n");
if (strcmp(move->stu.key ,"000000")==0)
{
printf("请输入您的密码:");
int i = 0;
while (1)
{
key[i] = getch();
if (key[i] == '\r')
break;
if (key[i] == '\b' && i > 0)
{
printf("\b");
_putch(' ');
printf("\b");
key[--i] = '\0';
continue;
}
if (key[i] == '\b' && i == 0)
{
printf("\b");
_putch(' ');
printf("\b");
i = -1;
continue;
}
i++;
printf("*");
}
key[i] = '\0';
if (strcmp(key, move->stu.key) == 0)/*不能学生输入的密码
还是初始密码,不然学生注册相当于没有完成,仍然登陆不上学生界面。*/
{
printf("\n您输入的密码为初始密码,不能用初始密码作为您的密码\n");
printf("即将返回主页面\n");
system("pause");
system("cls");
Menu();
}
if (strlen(key) < 6 || strlen(key) > 18) {/*控制密码长度位于
规定的范围内。*/
printf("\n请输入6-18位的密码!\n");
printf("即将返回主页面\n");
system("pause");
system("cls");
Menu();
}
strcpy(move->stu.key, key);
printf("\n注册成功!");
system("pause");
system("cls");
Menu();
}
else
{
printf("该生已完成注册,请勿重复注册!");
return;
}
}
move = move->next;
}
printf("系统中未查询到该名学生,请提醒管理员录入学生信息!");
system("pause");
system("cls");
}
学生注册是由于增加学生后学生密码为初始密码,学生必须先进行注册更改密码,不能使用初始密码进行登录,避免出现别人查看该学生信息的情况而进行的设立。(虽然有点鸡肋,但存在一定有意义!!!)
2.3.2学生界面
void Menu1(node* move)
{
printf("*********************************************\n");
printf("----------------【学生界面】-----------------\n");
printf("*********************************************\n\n");
printf("****************系统功能菜单*****************\n");
printf("* *\n");
printf("*\t\t┌───────────────┐\t *\n");
printf("*\t\t│1.查看个人信息 │\t *\n");
printf("*\t\t│2.更改密码\t│\t *\n");
printf("*\t\t│3.返回主界面\t│\t *\n");
printf("*\t\t└───────────────┘\t *\n");
printf("* *\n");
printf("******************欢迎使用*******************\n\n");
int n;
while (1)
{
printf("请输入您的选择:");
scanf("%d", &n);
switch (n)
{
case 1:
printf("姓名:%s\n", move->stu.name);
printf("班级:%s\n", move->stu.iclass);
printf("学号:%s\n", move->stu.num);
printf("语文:%d\n", move->stu.chinese);
printf("数学:%d\n", move->stu.math);
printf("英语:%d\n", move->stu.english);
printf("总分:%d\n", move->stu.sum);
break;
case 2:
printf("请输入您的新密码:");
scanf("%s", key);
strcpy(move->stu.key, key);
SaveInfo();
system("pause");
printf("更改成功!\n");
break;
case 3:
system("pause");
system("cls");
Menu();
break;
default:
printf("\n没有这个选项,请重新选择!\n");
break;
}
}
}
由于学生界面实现的功能比较简单,故而仅使用一个函数来进行实现。
该函数已经传入了该学生信息的节点,故而直接对该节点进行更改,而后密码保存一定还要存入文件中。
2.4教师界面
2.4.1班级成绩的排序
void OurClass(Node* Move, node* head)
{
node* move = head;
node* head1 = (node*)malloc(sizeof(node));
head1->next = NULL;
node* move1 = head1;//定义一个新的链表头
while (move->next != NULL)
{
if (strcmp( Move->tea.iclass ,move->stu.iclass ) == 0)
{
move1 = head1;
node* move2 = (node*)malloc(sizeof(node));
move2->next = NULL;
move2->stu = move->stu ;
while (move1->next != NULL)
{
move1 = move1->next;
}
move1->next = move2;
}
move = move->next;
}//将该班学生信息放置进新创建的链表顺序中
Bubble(head1);//冒泡排序
move1 = head1->next ;
printf("姓名\t班级\t\t学号\t语文\t数学\t英语\t总分\n");
while (move1/*->next*/ != NULL)
{
printf("%s\t%s\t\t%s\t%d\t%d\t%d\t%d\n", move1->stu.name, move1->stu.iclass, move1->stu.num,
move1->stu.chinese, move1->stu.math, move1->stu.english, move1->stu.sum);
move1 = move1->next;
}//打印该班学生信息
}
该函数即实现了不改变原来链表顺序下对于当前班级排序的功能,即将该班级学生消息取出放置在一个新的链表中,而后对于该链表进行排序即可。
由于排序使用函数与全年级学生排序使用函数相同,这里笔者只对该班总成绩进行排序,没有对单科成绩进行排序。
2.4.2教师密码更改
由于和学生本人更改自己密码操作相同,这里笔者不多做赘述。
三、收获和问题
3.1收获
- 对与链表的排序使用的更加熟练,将所学的快速,选择,冒泡三种链表的排序方法运用在该管理系统内,在实现功能同时也更好的熟练使用该几项排序。
- feof函数
feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0
3.2问题
- 许多地方没有考虑到逻辑的问题,没有在代码中警告出现一些不符合常理的情况。
- 有些功能的实现考虑并不全面,对于链表的使用熟练但并不精通有些地方的底层逻辑。