因为周边很多自学者,并且最近有朋友在写这个管理系统,所以就对之前随便写的做了完善,并且附上了详细的注释,希望对大家有所帮助,同时以后忘记了也可以回过头看看。话不多说,该注意的都在代码里了
#include <stdio.h>
#include <malloc.h>#include <Windows.h>#include <string.h>/*函数声明*/void shouye();void AddMessage();void chazhao();void shanchu();void duqu();int panduan();int denglu();void Error();//定义一个学生的数据类型struct Student{ char name[20]; char num[20]; struct Student *next;};//主函数int main(void){ shouye(); //调用首页函数 return 0;}void Error() //当一个东西反复使用多于两次时,就考虑用函数来实现。相同的代码不多写,只调用函数{ printf("输入错误\n"); //提示错误 system("pause"); //暂停一下给用户看,让用户知道错误了}//首页函数void shouye(void){ char XuanZe[20] = {0}; //定义 while(1) { system("cls"); //打印首页前应该先清屏,清除之前的操作 printf("********这是首页********\n"); printf("* 1、添加学生信息 *\n"); printf("* 2、查找学生信息 *\n"); printf("* 3、删除学生信息 *\n"); printf("* 4、打印学生信息 *\n"); printf("* 5、退出首页 *\n"); printf("************************\n"); printf("\n请输入您想要执行操作的序号:"); scanf("%s",XuanZe); //字符串输入不需要&符。 if(strlen(XuanZe) > 1) //如果输入的选项长度大于1,提示错误函数提示错误并continue重新清屏打印首页 { Error(); continue; } switch(XuanZe[0]) { case '1':AddMessage();break; //调用添加函数并break case '2':chazhao();break; //调用查找函数并break case '3':shanchu();break; //调用删除函数并break case '4':duqu();break; //调用读取函数并break case '5':return ; //退出首页 default:Error();break; //调用错误函数提示错误 } }}//添加信息函数void AddMessage(){ struct Student *pHead = NULL; //链表环节,链表和数组只是两种不同的储存方式而已,哪种方便用哪种,更应该思考的是如何去使用任意一种实现功能 struct Student *p1,*p2; char flag[2] = "0"; p1 = p2 = (struct Student*)malloc(sizeof(struct Student)); if(p1 == NULL || NULL == p2) { printf("内存分配失败\n"); return ; } printf("请输入学生的姓名,学号,用空格隔开,姓名为0时终止输入:"); //提示用户究竟怎样输入 scanf("%s%s",p1->name,p1->num); while(strcmp(p1->name,flag) != 0 ) //输入环节,名字为0则退出 { if(!panduan(p1)) //调用判断函数查询学号是否重复。如果这个输入的学号已经存在了,退出写入的模块。(之前符合条件的已经被写入函数了) break; //详见判断函数 if(NULL == pHead) pHead = p1; else p2->next = p1; p2 = p1; p1 = (struct Student*)malloc(sizeof(struct Student)); if(p1 == NULL) { printf("内存分配失败\n"); return ; } printf("请输入学生的姓名,学号,用空格隔开,姓名为0时终止输入:"); scanf("%s%s",p1->name,p1->num); } free(p1); p1 = NULL; p2->next = NULL; system("pause"); return ;}//判断函数,判断写入的学号是否已经存在int panduan(struct Student *p2) //使用结构体指针作为参数,因为考虑到要把结构体内容都写入文件{ char num[100] = {0},name[20] = {0}; FILE *fp = fopen("E:\\student.txt","r"); //以只读的方式打开文件 while(fscanf(fp,"%s%s",num,name) != EOF) //考虑到存储是:学号 姓名\n 这样的格式。fscanf不会读入空格和回车,类似这样写。正好每次读取一行,每行的学号在num数组里,姓名在name数组里。如果信息很多也可采取此办法 { if(strcmp(p2->num,num) == 0) //若存在,提示错误信息并关闭文件返回 { printf("此学号已经存在,非法操作\n"); fclose(fp); //关闭文件 小技巧:只要打开了文件,在return语句前一定要有一个关闭文件 return 0; } } fclose(fp); //先关闭,再用追加的方式打开。此时文件指针位于文件末尾 fp = fopen("E:\\student.txt","a+"); fprintf(fp,"%s %s\n",p2->num,p2->name); //如果遍历到文件末尾没找到一样的,证明这个学号是可行的,写入。否则一定会在中途找到后就结束此函数。写入的方式是:学号 姓名\n。有便于我们查找。 fclose(fp); return 1;}//查找函数void chazhao(void){ char xuehao[20] = {0},name[20] = {0},num[20]; FILE *fp = fopen("E:\\student.txt","r"); //查找,所以用只读方式打开 system("cls");//从首页跳转到查找函数,应该清屏 printf("请输入您想要查找的同学学号:"); scanf("%s",num); while(fscanf(fp,"%s%s",xuehao,name)!=EOF) //如果没有到末尾,就继续读取这一行的学号和名字,分别放到数组里,之前的会被覆盖,两个数组可循环使用,减少内存消耗 { if(strcmp(num,xuehao) == 0) //遍历整个文件。如果找到了,输出该同学的信息,并结束查找函数。记得关闭文件 { printf("您所查找的同学信息如下:\n"); printf("姓名:%s 学号:%s\n",name,xuehao); system("pause"); fclose(fp); return ; } } printf("查无此人\n"); //如果程序能运行到该语句,证明遍历完也没找到这个人,所以打印提示信息。暂停,并关闭文件 system("pause"); fclose(fp); return ;}//删除函数void shanchu(void){ int i = 0,k = 0,flag = 0; char xuehao[100] = {0},name[20] = {0},num[20],xin[500][50] = {0}; //用xin这个二维数组保存删除后整个文件内容,并重新写入文件更换掉原文件 FILE *fp = fopen("E:\\student.txt","r"); if(!denglu()) //如果登陆失败,退出。 return ; duqu(); //调用读取函数,将文件名单展示出来,方便输学号 printf("\n请输入您想要删除的同学学号:"); scanf("%s",num); while(fscanf(fp,"%s%s",xuehao,name)!=EOF) { if(strcmp(xuehao,num) == 0) //找到该同学,此次读取到的学号和名字不写入xin数组。 { flag = 1; continue; } else //删除同学前的都写入xin数组,当遍历到那个同学的时候,没有写入,继续读取下一行写入xin数组,这就相当于将那位同学的信息没有存进来。最后将xin数组保存,自然就没有那位同学了 { strcat(xin[k],xuehao); //先把学号连接到xin[k]里,然后是一个空格,然后是名字,最后是换行符。strcat会自动在最后加上\0符 strcat(xin[k]," "); strcat(xin[k],name); strcat(xin[k],"\n"); k++; //记录这个数组存了多少个人。等下直接遍历到k个就行了 } } if(flag == 0) //如果为0,证明删除的学号不存在 { printf("您要删除的的人不存在\n"); system("pause"); fclose(fp); return ; } else //这里表示删除的人在里面 { fclose(fp); //先关闭文件,再用只写的方式打开,这样做的后果是原来文件里的东西都会丢失,不管原来里面有什么,打开后里面都是空白的 fp = fopen("E:\\student.txt","w"); for(i = 0;i <= k;i++) //用循环遍历到k,将每个i位置的学号 名字\n写入文件里 最后文件里的格式就是每个同学的占一行了 { fprintf(fp,"%s",xin[i]); } printf("删除完成!\n"); //提示用户删除完成了,关闭文件结束操作 fclose(fp); system("pause"); } duqu(); //将删除后的文件再读取一遍展现给用户,更具人性化吧。。。。 fclose(fp); return ;}//删除操作时登陆函数int denglu(void) //删除属于敏感操作,可考虑加上一个登陆限制。返回值是返回一个flag,是否成功登陆{ char zhanghao[20] = "唐小龙",zhanghao1[20] = {0}; //账号名和输入的账号名 int mima; //密码 printf("请输入管理员账号:"); scanf("%s",zhanghao1); if(strcmp(zhanghao,zhanghao1) == 0) //如果账号正确,则验证密码 { printf("请输入密码:"); scanf("%d",&mima); if(520 != mima) { system("cls"); //清屏 printf("密码错误!\n"); system("pause"); return 0; //登陆失败,返回0 } } else //账号错误,提示错误信息并返回,避免多次试验盗密码。。。。这里其实还可以让其重试,限制重试次数。超过次数就返回。后者更好 { system("cls"); printf("账号错误!\n"); system("pause"); return 0; //登陆失败,返回0 } printf("成功登陆!\n"); system("pause"); return 1; //成功登陆,返回1}//打印信息函数void duqu(void){ char num[20] = {0},name[20] = {0}; FILE *fp = fopen("E:\\student.txt","r"); //其实文件都有可能打开失败(比如文件被某些东西占用)导致后续操作出现不可预知的错误,所以检测是很有必要的。不过有些地方省略了 if(fp == NULL) { printf("文件打开失败\n"); return ; } system("cls"); //读取信息前先清屏 printf("\n目前已有的学生信息如下:\n"); while(fscanf(fp,"%s%s",num,name)!=EOF) //读取文件中一行学生的学号和姓名,直到文件结束(EOF) printf("姓名:%s 学号:%s\n",name,num); //将读取到的信息打印出来。如果是读取完再打印到屏幕,未免太占内存了。这里还是反复使用两个数组 fclose(fp); printf("\n"); system("pause");}