因为周边很多自学者,并且最近有朋友在写这个管理系统,所以就对之前随便写的做了完善,并且附上了详细的注释,希望对大家有所帮助,同时以后忘记了也可以回过头看看。话不多说,该注意的都在代码里了
#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("输入错误
"); //提示错误
system("pause"); //暂停一下给用户看,让用户知道错误了
}
//首页函数
void shouye(void)
{
char XuanZe[20] = {0}; //定义
while(1)
{
system("cls"); //打印首页前应该先清屏,清除之前的操作
printf("********这是首页********
");
printf("* 1、添加学生信息 *
");
printf("* 2、查找学生信息 *
");
printf("* 3、删除学生信息 *
");
printf("* 4、打印学生信息 *
");
printf("* 5、退出首页 *
");
printf("************************
");
printf("
请输入您想要执行操作的序号:");
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("内存分配失败
");
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("内存分配失败
");
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) //考虑到存储是:学号 姓名
这样的格式。fscanf不会读入空格和回车,类似这样写。正好每次读取一行,每行的学号在num数组里,姓名在name数组里。如果信息很多也可采取此办法
{
if(strcmp(p2->num,num) == 0) //若存在,提示错误信息并关闭文件返回
{
printf("此学号已经存在,非法操作
");
fclose(fp); //关闭文件 小技巧:只要打开了文件,在return语句前一定要有一个关闭文件
return 0;
}
}
fclose(fp); //先关闭,再用追加的方式打开。此时文件指针位于文件末尾
fp = fopen("E:\student.txt","a+");
fprintf(fp,"%s %s
",p2->num,p2->name); //如果遍历到文件末尾没找到一样的,证明这个学号是可行的,写入。否则一定会在中途找到后就结束此函数。写入的方式是:学号 姓名
。有便于我们查找。
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("您所查找的同学信息如下:
");
printf("姓名:%s 学号:%s
",name,xuehao);
system("pause");
fclose(fp);
return ;
}
}
printf("查无此人
"); //如果程序能运行到该语句,证明遍历完也没找到这个人,所以打印提示信息。暂停,并关闭文件
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("
请输入您想要删除的同学学号:");
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会自动在最后加上 符
strcat(xin[k]," ");
strcat(xin[k],name);
strcat(xin[k],"
");
k++; //记录这个数组存了多少个人。等下直接遍历到k个就行了
}
}
if(flag == 0) //如果为0,证明删除的学号不存在
{
printf("您要删除的的人不存在
");
system("pause");
fclose(fp);
return ;
}
else //这里表示删除的人在里面
{
fclose(fp); //先关闭文件,再用只写的方式打开,这样做的后果是原来文件里的东西都会丢失,不管原来里面有什么,打开后里面都是空白的
fp = fopen("E:\student.txt","w");
for(i = 0;i <= k;i++) //用循环遍历到k,将每个i位置的学号 名字
写入文件里 最后文件里的格式就是每个同学的占一行了
{
fprintf(fp,"%s",xin[i]);
}
printf("删除完成!
"); //提示用户删除完成了,关闭文件结束操作
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("密码错误!
");
system("pause");
return 0; //登陆失败,返回0
}
}
else //账号错误,提示错误信息并返回,避免多次试验盗密码。。。。这里其实还可以让其重试,限制重试次数。超过次数就返回。后者更好
{
system("cls");
printf("账号错误!
");
system("pause");
return 0; //登陆失败,返回0
}
printf("成功登陆!
");
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("文件打开失败
");
return ;
}
system("cls"); //读取信息前先清屏
printf("
目前已有的学生信息如下:
");
while(fscanf(fp,"%s%s",num,name)!=EOF) //读取文件中一行学生的学号和姓名,直到文件结束(EOF)
printf("姓名:%s 学号:%s
",name,num); //将读取到的信息打印出来。如果是读取完再打印到屏幕,未免太占内存了。这里还是反复使用两个数组
fclose(fp);
printf("
");
system("pause");
}