学生管理系统总结
登录注册
首先定义一个结构体用来存储用户的账号和密码,如下:
typedef struct Account {
char accnum[20];
char key[20];
struct Account* next;
}Acc;
void sturegister(node* head) {
printf("欢迎来到学生管理系统\n请先注册\n");
int count = 0,flag = 0;
char accnum[6] = {0}, key[6] = {0};
printf("---------------------------------------注册--------------------------------------------\n");
printf("请输入要注册的学生账号及密码\n注:密码和账号均需六位数\n");
scanf("%s",accnum);
fflush(stdin);
scanf("%s",key);
fflush(stdin);
FILE *fp=fopen("/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/注册账号及密码.txt","rt+");
if (fp == NULL)//判断文件是否为空,为空则退出
{
exit(-1);
} else {
while (!feof(fp)) {//遍历整个文件
char accnum1[6] = {0};
char password[6] = {0};
fscanf(fp,"%s %s",accnum1, password);//读取文件中存储的账号和密码
if (strcmp(accnum1,accnum) == 0)//将从文件中读取到的账号和想要注册的账号进行比较
{
printf("账号已存在");
flag = 1;
fclose(fp);
choice(head);
}
}
if(flag == 0) {
fp = fopen("/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/注册账号及密码.txt","at+");
fprintf(fp,"%s %s\n",accnum,key);//将新注册的账号和密码写入文件中
fclose(fp);
printf("注册成功,账号为:%s,密码为:%s\n",accnum,key);
stuLogin(head);
count++;
}
}
}
文件的读取
因为课设文件里已经把五个班的信息已经给了我,所以我从一开始就在思考该如何把这五个班信息的文件读取在链表里,然后再便于后续的增删改查的操作
首先,需要定义一个链表,便于后续将文件中每一个人的成绩放进链表,如下:
typedef struct student {
char num[100];
char name[100];
char class[10];
float score[3];
struct student* next;
}node;
学生界面的菜单:
void stuMenu(node*head) {
newread(head);//读取文件,将文件中的内容放在链表中,以便后续的查询等操作;
printf("欢迎使用学生界面\n");
printf("系统菜单\n");
printf("1:成绩查询");
printf("2:查询本班成绩");
printf("3:成绩分析");
printf("4:退出");
printf("请输入菜单编号\n");
char x[100];//定义字符串而不是整型,详见注意事项1;
scanf("%s",x);
fflush(stdin);//清缓冲区
if (strlen(x) == 1) {//通过字符串的长度来控制输入的字符串
if(x[0] == '1') {
seeksearch(head);
}
if(x[0] == '2') {
systemLogin(head);
}
if(x[0] == '3') {
scoreAnalyze(head);
}
if(x[0] == '4') {
mainMenu(head);
}
} else {
printf("请重新输入1-4的数:\n");
mainMenu(head);
}
}
注意事项1:
输入菜单的序号时采用字符串而不是整型,是因为定义为整型时输入一旦错误程序就会退出,而定义字符串容错率较高。
注意事项2:
if可以判断字符串,而switch不可以用来判断字符串。
注意事项3:
清缓冲区,这是因为在你有的时候在输入一个字符后,在输入一个字符,如果你不清空缓冲区,那上一个字符还在你的缓冲区内!这样就造成错误了!
C语言中的缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。
C语缓冲区分为三种类型:1、全缓冲 2、行缓冲 3、不带缓冲。 缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。例如,在我们平时要在磁盘中读取信息的情况下,先会把数据放到缓存区中,读取完后,再次从磁盘中读取信息。
缓存区,他的意义就是在高速CPU与低速的设备之间的一个区域,这个区域让CPU工作效率更高。全缓冲 当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
行缓冲
当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout)。不带缓冲 也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
大部分系统默认使用下列类型的缓存: 标准出错是不带缓存的。 如果是涉及终端设备的流,则它们是行缓存的,否则是全缓存的。
我们经常用到的输入输出流,在目前的ANSI C 中缓存的特征是:stdin和stdout是行缓存;而stderr是无缓存的。
fflush()函数
fflush()函数冲洗流中的信息,该函数通常用于处理磁盘文件。清除读写缓冲区,需要立即把输出缓冲区的数据进行物理写入时。fflush()函数包含在stdio.h头文件中。
函数的返回值:当进行刷新成功返回0,失败返回EOF。没有缓冲区或者只读打开时也返回0值。
还有需要注意的是:如果fflush返回EOF,数据可能由于写错误已经丢失。
用法示例:fflush(stdin)刷新标准输入缓冲区,fflush(stdout)刷新标准输出缓冲区。 printf(“。。。。。。。。。。。”);后面加fflush(stdout);可提高打印效率。
将链表中的信息保存到文件中:
void newsave(node *head) {
node* p = head;
FILE* fp;
char filename[40];
if ((fp = fopen("/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/学生信息/核——学生管理系统2020/学生信息/class1.txt","wt")) == NULL) {
char k;//打开文件,文件指针为空则打开失败,退出当前页面;
printf("写文件出错,按任意键退出!");
scanf("%c",&k);
menu(head);
}
for (p = head->next; p; p = p->next) {//遍历整个链表
fprintf(fp,"%s %s %s %f %f %f\n",p->num,p->name,p->class,p->score[0],p->score[1],p->score[2]);//将链表中的信息写入文件中进行保存;
}
fclose(fp);//关闭文件
printf("\n文件已保存,按任意键返回菜单");
char k;
scanf("%c",&k);
menu(head);
}
这里文件的保存操作是指:将链表中的学生信息写入文件中从而进行保存;
这里要注意对文件的打开操作,写操作以及关闭操作;
将文件中的信息读取到链表中:
void newread(node* head) {
node *r, *s;
FILE* fp;
char filename[40];
printf("请选择你想要读取的班级:\n");
fflush(stdin);
char x[100];
char temp[1000];//通过变量temp,可以随意地在五个文件中选择性的读取,而不是只能读取一个文件;
scanf("%s",x);
fflush(stdin);
if (strlen(x) == 1) {
if(x[0] == '1') {
strcpy(temp,"/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/学生信息/核——学生管理系统2020/学生信息/class1.txt");
}
if (x[0] == '2') {
strcpy(temp,"/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/学生信息/核——学生管理系统2020/学生信息/class2.txt");
}
if (x[0] == '3') {
strcpy(temp,"/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/学生信息/核——学生管理系统2020/学生信息/class3.txt");
}
if (x[0] == '4') {
strcpy(temp,"/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/学生信息/核——学生管理系统2020/学生信息/class4.txt");
}
if (x[0] == '5') {
strcpy(temp,"/Users/zhangjiahui/Desktop/学生管理系统/学生管理系统/学生信息/核——学生管理系统2020/学生信息/class5.txt");
}
} else {
printf("输入错误:\n");
stuMenu(head);
}
if ((fp = fopen(temp
,"r")) == NULL) {
char k;
printf("读文件出错,按任意键退出!");
scanf("%c",&k);
menu(head);
}
node* p = (node*)malloc(sizeof(node));
p = head;
p->next = NULL;
r = p;
while(!feof(fp)) {
s = (node*)malloc(sizeof(node));
fscanf(fp,"%s %s %s %f %f %f\n",s->num,s->name,s->class,&s->score[0],&s->score[1],&s->score[2]);//将文件中的信息读取到链表中;
r->next = s;
s->next = NULL;
r = r->next;
}
fclose(fp);//关闭文件
}
因为在读入时已经将每个人的信息构成了一个链表,后续只需要对此时的链表进行增删改查等操作就好了。
排序
对链表进行冒泡排序:
void sort(node *head)//将学生按照c语言成绩排序 {
newread(head);
printf("您选择了按照c语言成绩排序排序操作\n");
int i, count = 0, num;
node* p, *q, *t;
p = head->next;
if (p == NULL) {
printf("信息不完整,排序无法完成\n");
menu(head);
} else {
while (p) {
p = p->next;
count++;
}
for (i = 0 ;i < count-1 ;i++) {
num = count-1-i;
q = head->next;
t = head;
p = q->next;
while (num--) {
if(q->score[0] > p->score[0]) {
q->next = p->next;
p->next = q;
t->next = p;
}
t = t->next;
q = t->next;
p = q->next;
}
}
printf("排序已完成\n");
while (p) {
printf("学生学号 * 姓名 * 班级 * 年龄 * c语言成绩 * 高数成绩 * 英语成绩:\n");
printf("-------------------------------------------------------\n");
printf("%s %s %s %f %f %f\n",p->num,p->name,p->class,p->score[0],p->score[1],p->score[2]);
p = p->next;
}
}
menu(head);
}
插入排序:
stu *view_sort_math(stu *head) {
struct student *first;
struct student *t;
struct student *p;
struct student *q;
first = head->next;
head->next = NULL;
while (first != NULL) {
for (t = first, q = head; ((q != NULL) && (q->chinese > t->chinese)); p = q, q = q->next);
first = first->next;
if (q == head) {
head = t;
} else {
p->next = t;
}
t->next = q;
}
return head;
}
这里是对单链表的冒泡排序,插入排序,还可以对单链表进行选择排序和快速排序;
总结
- 我认为对文件读写比较关键,在能正常对文件信息读取后,后续操作就显得简易了许多。后续链表的增删改查,对链表存储信息的分析都是建立在文件信息内容能一字不落的读入的前提下。通过写学生管理系统,我掌握了对文件的有关操作,能够用代码来实现,而不是像之前仅限于理论知识的了解了,与此同时,也复习和巩固了我对链表的增删改查的基本操作,也是我第一次相对完整的写好了一个小的程序!