对前面信息管理系统的完善

来源:互联网 发布:大数据涂子沛书籍下载 编辑:程序博客网 时间:2024/05/17 04:41
        因为周边很多自学者,并且最近有朋友在写这个管理系统,所以就对之前随便写的做了完善,并且附上了详细的注释,希望对大家有所帮助,同时以后忘记了也可以回过头看看。话不多说,该注意的都在代码里了

 

#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");
}
0 0