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


      因为周边很多自学者,并且最近有朋友在写这个管理系统,所以就对之前随便写的做了完善,并且附上了详细的注释,希望对大家有所帮助,同时以后忘记了也可以回过头看看。话不多说,该注意的都在代码里了

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



  • 相关阅读:
    归并排序
    CTE 递归
    Cordova 入门文档
    Javascript 原型链
    Windows11 正式版偷渡开启安卓子系统
    快速解决gerrit merge confict问题
    利用VPS来搭建个人主页
    检测串行序列10010
    Verilog语法总结
    深度学习中常见优化算法学习笔记
  • 原文地址:https://www.cnblogs.com/YaLi/p/6399745.html
Copyright © 2020-2023  润新知