您的位置:首页 > 其它

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

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

 

#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这个二维数组保存删除后整个文件内容,并重新写入文件更换掉原文
4000


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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: