程序设计基础(C)大作业——学籍管理系统(3)(完)

版权声明:本文为Cantjie原创文章,转载需获得博主许可 https://blog.csdn.net/cantjie/article/details/71244651

最后,将写完的代码贴上来

一点说明:
1、目前还有一些bug,在ID被判重复后输入0,有时会使得程序崩溃,时间原因尚未解决
2、中英文的注释混杂,并不是一个好习惯。写的过程中试图让自己以全英文注释,并严格按格式注释。但由于时间关系或英文水平的限制,既没有按照格式来注释,也没有做到全英文。
3、链表的建立、validate并不是很让我满意,感觉还有可以优化的地方。
4、sort用了选择排序,find只是逐个比较来查找,并没有学到什么新的算法,也没有搜一搜适合链表的查找算法。这里或许可以做很多优化。
5、文件的读写过于频繁。还不会随机读写,或许这里也可以进一步优化。
6、之所以主界面里面没有单独的排序功能,是因为添加、更新数据后都会重新排序一次并保存在文件中。
7、交互界面并不令我满意。很简陋。
8、在前期没有把函数的实现方法想明白,导致main.c中controller函数很复杂。或许可以改进一下delete、update等函数,使得代码更简洁,重复代码更少。并且使得variance、delete、update等函数用法更加一致。
9、程序写完之后才意识到没有输出均值,于是临时添加全局变量m_ave。
10、在学生姓名发生重复时,无法同时查找到这些学生。

——————————————

20170508修复了update时无法更新的问题。

——————————————

20170520结构体可以用=直接赋值,可以将swap函数省略了但还没有改

——————————————

app.c

#include "final.h"


/**
*selectsort by cls & sub-by name
*by cantjie
*/
stu *sort(stu *head)
{
    //遍历链表,每次找出一个最小的节点,将其值与未排序节点的首个节点交换,这里需要一个指针标记值最小的节点。
    stu *p1, *p2, *min;
    for (p1 = head->next; p1->next != NULL; p1 = p1->next)
    {
        min = p1;
        for (p2 = p1->next; p2; p2 = p2->next)//由前向后遍历,找出最小的节点
        {
            if (p2->cls<min->cls)
                min = p2;
            else
            {
                if (p2->cls == min->cls)
                {
                    if (strcmp(p2->name, p1->name) < 0)
                    {
                        swapStu(p1, p2);
                    }
                }
            }
        }
        if (min != p1)
            swapStu(p1, min);
    }
    saveStu(head->next, 0);
    m_head = head;
    return head;
}


/**
*检查id 是否重复,如果id重复,返回1。不重复返回0
*/
int validate(stu *head, int id)
{
    stu *p = head;
    int flag = 0;//因为会遇到自己,设置一个flag,当flag为2时表示有重复;
    while (p->next)
    {
        if (p->next->id == id)
        {
            if (flag)
                return 1;
            flag++;
        }
        p = p->next;
    }
    if (m_head != head)
    {
        p = m_head;
        if (p == NULL)
            return 0;
        while (p->next)
        {
            if (p->next->id == id)
                return 1;
            else
                p = p->next;
        }
        return 0;
    }

}

/**
*计算方差,包含三种格式:
*1、输入0,返回所有学生方差;
*2、输入1,输入cls,返回该班学生的方差;
*3、输入2,然后输入id范围,返回方差;
*by cantjie
*/
float varianceStu(stu *head)
{

    int flag = -1;
    int n = 0;
    int idmax=0, idmin=0;//for case 2
    int id = 0;//for case 3
    float var=0, sum=0, ave=0;
    int i = 0;
    stu *p = head;
    printf("\ninput 0 to get all students' variance");
    printf("\ninput 1 to get variance of students of a class");
    printf("\ninput 2 to get varicance of students of a range of ids\n");
    scanf("%d", &flag);
    switch (flag)
    {
    case 0://全体方差
        for (i = 0; i < m_n; i++)
        {
            sum += p->next->sum;
            p = p->next;
        }
        ave = sum / m_n;
        p = head;
        for (i = 0; i < m_n; i++)
        {
            var += (p->next->sum - ave)*(p->next->sum - ave);
            p = p->next;
        }
        var /= m_n;
        break;
    case 1://返回班级方差
        printf("\nPlease input class:");
        int cls = 0;
        scanf("%d", &cls);
        for (i = 0; i < m_n; i++)
        {
            if (p->next->cls == cls)
            {
                sum += p->next->sum;
                n++;
            }
            p = p->next;
        }
        ave = sum / n;
        p = head;
        for (i = 0; i < m_n; i++)
        {
            if (p->next->cls == cls)
            {
                var += (p->next->sum - ave)*(p->next->sum - ave);
            }
            p = p->next;
        }
        var /= n;
        break;
    case 2://id范围
        printf("\nPlease input the range of ID:(from...to...)");
        scanf("%d%d", &idmin, &idmax);
        if (idmin > idmax)
            return 0;
        p = head; n = 0;
        for (i = 0; i < m_n; i++)
        {
            if (p->next->id >= idmin && p->next->id <= idmax)
            {
                sum += p->next->sum;
                n++;
            }
            p = p->next;
        }
        ave = sum / n;
        p = head;
        for (i = 0; i < m_n; i++)
        {
            if (p->next->id >= idmin && p->next->id <= idmax)
            {
                var += (p->next->sum - ave)*(p->next->sum - ave);
            }
            p = p->next; 
        }
        var /= n;
        break;

        //这个功能有问题。时间不够,放弃了
/*  case 3://连续输入ID
        printf("\nPlease input student id:(input 0 to stop input)");
        while (scanf("%d", &id))
        {
            if (id == 0) break;
            for (i = 0; i < m_n; i++)
            {
                if (p->next->id == id)
                {
                    n++;
                    sum += p->next->sum;
                }
                p = p->next;
            }
            ave = sum / n;
            p = head;
            for (i = 0; i < m_n; i++)
            {
                if (p->next->id == id)
                {
                    var += (p->next->sum - ave)*(p->next->sum - ave);
                }
            }
            var /= n;
        }
        break;*/
    default:
            break;
    }
    m_ave=ave;
    return var;
}

/**
*by cantjie
*/

stu *create()//创建或写入新的学生数据
{
    int i = 0;
    stu *head = NULL;//指向链表头,链表头一般不存信息。
    stu *p1 = NULL, *p2 = NULL;//p1指向新创建的链表,p2是原链表的尾部
    p1 = (stu *)malloc(LEN);
    if (p1 == NULL)
    {
        printf("\nFail to create it,please try again later.\n");
        return NULL;
    }
    head = p1;
    head->id = -1;

    p2 = p1 = (stu *)malloc(LEN);
    if (p1 == NULL)
    {
        printf("\nFail to create it,please try again later.\n");
        return NULL;
    }
    else
    {
        head->next = p1;
        p1->next = NULL;
        printf("\nPlease input student ID(0 to stop):");
        scanf("%d", &(p1->id));
    }
    if (p1->id == 0)
    {
        return 0;
    }
    while (p1->id != 0)
    {

        p2->next = p1;
        p1->next = NULL;//通过调试可以发现如果不加这两句命令,连着输入两次相同ID,validate无法检测出来
        if (validate(head, p1->id))
        {
            printf("\nThis id has already existed,please try again.");
            printf("\nPlease input student ID(0 to stop):");
            scanf("%d", &(p1->id));
            if (p1->id == 0) p2->next = NULL;//与上面那句命令对应
            continue;
        }
        else
        {
            p2->next = p1;
            p2 = p1;
            printf("\nPlease input class:");
            scanf("%d", &(p1->cls));
            printf("\nPlease input name(no more than 20 characters):");
            //scanf("%s", p1->name); 如果用scanf的话,无法保存空格
            fflush(stdin);//当gets前面有scanf时,scanf余留下来的换行符会被gets读取,因此用fflush(stdin)刷新缓存区。
            gets(p1->name);
            printf("\nPlease input scores of 3 subjects:");
            p1->sum = 0;
            for (i = 0; i < 3; i++)
            {
                scanf("%f", &(p1->score[i]));
                p1->sum += p1->score[i];
            }
            m_n++;
            p2->next = NULL;
            p1 = (stu *)malloc(LEN);//下面free的是这个p1
            if (p1 == NULL)
            {
                printf("\nFail to create it,please try again later.\n");
                return NULL;
            }
            else
            {
                printf("\nPlease input student ID(0 to stop):");
                scanf("%d", &(p1->id));
            }
        }

    }
    free(p1);
    saveStu(head->next, 1);
    return sort(readStu());
}


/**
*显示学生信息
*/
void showStu(stu *p)
{
    if (p == NULL)
    {
        printf("No such a student");
        return;
    }
    printf("\n%-20s|%5d|%5d|%6.1f|%6.1f|%6.1f|%5.1f", p->name, p->id, p->cls, p->score[0], p->score[1], p->score[2], p->sum);
}




/**
*删除学生信息
*传入的参数为要删除的学生的前一个学生的指针
*by cantjie
*/
void deleteStu(stu *p)
{
    if (p == NULL)
    {
        printf("\nNo such a student.");
        return;
    }
    stu *temp = p->next;
    p->next = p->next->next;
    free(temp);
    saveStu(m_head->next, 0);
    m_n--;
}


/**
*更新、修改学生信息
*传入的参数为要更改的学生的指针
*by cantjie
*/
void updateStu(stu *p)
{
    if (p == NULL)
    {
        printf("\nNo such a student");
        return;
    }
    showTitle();
    showStu(p);
    int i, temp, temp2;
    printf("\nPlease input student ID:");
    scanf("%d", &temp);
    if (temp == 0)
        return;
    if (temp != 0 && temp != p->id)
    {
        while (validate(m_head, temp))
        {
            printf("\nThis id has already existed,please try again.");
            printf("\nPlease input student ID(0 to stop):");
            scanf("%d", &temp);
            if (temp == 0)
                return;
            if (temp == p->id)
                break;
        }
    }
    p->id = temp;

    printf("\nPlease input class:");
    scanf("%d", &(p->cls));
    printf("\nPlease input name(no more than 20 characters):");
    fflush(stdin);//当gets前面有scanf时,scanf余留下来的换行符会被gets读取,因此用fflush(stdin)刷新缓存区。
    gets(p->name);
    printf("\nPlease input scores of 3 subjects:");
    p->sum = 0;
    for (i = 0; i < 3; i++)
    {
        scanf("%f", &(p->score[i]));
        p->sum += p->score[i];
    }
    saveStu(m_head->next, 0);
    sort(m_head);
}

——————————————

common.c


#include "final.h"



/**
*通过学号或者姓名查找学生,用在其他函数中
*返回该学生前一个学生的指针
*重名问题暂时还没有解决,只能返回一个学生
*by cantjie
*/
stu *findStu(stu *head, int id, char *name)
{
    if (id == 0)
    {
        while (head->next)
        {
            if (strcmp(head->next->name, name) == 0)
                return head;
            else
                head = head->next;
        }
        //printf("Cannot find a student named %s", name);
        return NULL;
    }
    else
    {
        while (head ->next)
        {
            if (head->next->id == id)
                return head;
            else
                head = head->next;
        }
        //printf("Cannot find a student whose id is %d", id);
        return NULL;
    }
    return 0;
}

/**
*swap the two Stu structure
*by cantjie
*/
void swapStu(stu *p1, stu *p2)
{
    int tempi;
    float tempf;
    char tempc[20];
    int i = 0;

    tempi = p1->id;
    p1->id = p2->id;
    p2->id = tempi;

    tempi = p1->cls;
    p1->cls = p2->cls;
    p2->cls = tempi;

    strcpy(tempc, p1->name);
    strcpy(p1->name, p2->name);
    strcpy(p2->name, tempc);

    for (i = 0; i < 3; i++)
    {
        tempf = p1->score[i];
        p1->score[i] = p2->score[i];
        p2->score[i] = tempf;
    }

    tempf = p1->sum;
    p1->sum = p2->sum;
    p2->sum = tempf;


}

——————————————

file.c


#include "final.h"


/**
*每次删除或更新等操作后,要把内存里的数据写入文件,flag表示模式,为1表示追加,为0表示重写
*put data in memory into _FILENAME_ in harddisk,used every time update or delete the data
*by cantjie
*/
void saveStu(stu *head, int flag)
{
    if (!head)
    {
        return;
    }
    FILE *fp = NULL;
    if (flag == 1)
        fp = fopen(_FILENAME_, "ab");
    else
    {
        if (flag == 0)
            fp = fopen(_FILENAME_, "wb");
    }
    if (fp == NULL)
    {
        fp = fopen(_FILENAME_, "wb");
        if (fp == NULL)
        {
            printf("\nCannot open or create the file");
            return;
        }
    }
    do
    {
        fwrite(head, sizeof(stu), 1, fp);
        head = head->next;
    } while (head);
    fclose(fp);

}

/**
*把文件里的数据读取到内存中
*put data in harddisk into memory
*by cantjie
*/
stu *readStu()
{
    stu *head = NULL, *tail = NULL, *p1 = NULL;//tail是原链表的尾,p1是新开辟的空间
    FILE *fp;
    fp = fopen(_FILENAME_, "rb");
    if (fp == NULL)
    {
        fp = fopen(_FILENAME_, "wb");
        if (fp == NULL)
            printf("Fail to open or create %s in function readStu", _FILENAME_);
        return NULL;
    }

    m_n = 0;
    p1 = (stu *)malloc(LEN);
    if (p1 == NULL)
    {
        printf("Fail to allocate memory for a new stu structure in function readStu.");
        fclose(fp);//如果没有成功开辟内存,还要把文件关闭
        return 0;
    }

    head = tail = p1;
    tail->next = NULL;
    while (!feof(fp))
    {
        p1 = (stu *)malloc(LEN);
        if (p1 == NULL)
        {
            printf("Fail to allocate memory for a new stu structure in function readStu.");
            fclose(fp);//如果没有成功开辟内存,还要把文件关闭
            return 0;
        }
        if (!fread(p1, sizeof(stu), 1, fp))
        {
            free(p1);
            return head;
        }
        tail->next = p1;
        tail = p1;
        tail->next = NULL;//保证尾指向空
        m_n++;
    }
    fclose(fp);
    m_head = head;
    return head;
}

——————————————

main.c

#include "final.h"
#include<math.h>

/**
*显示学生信息前显示表头
*/
void showTitle()
{
    printf("\n%-20s|%5s|%5s|score1|score2|score3|sum", "name", "ID", "class", "score1", "score2", "score3", "sum");
}

/**
*用在main函数,输入功能后进入此函数
*/
void controller(int flag)
{ //通过这里面的代码,或许可以考虑重新写一下findStu或重写deleteStu,updateStu
    int i = 0;
    int a = 0;
    stu *temp;
    float var;
    char name[20];
    switch (flag)
    {
    case 1:
        if (m_head == NULL || m_head->next == NULL)
        {
            printf("\nNo student data.");
            break;
        }

        temp = m_head;
        printf("\ninput 0 to show all students' information");
        printf("\nor input 1 and then an id or 2 and then a name to show one:");
        scanf("%d", &a);
        if (a == 0)
        {
            showTitle();
            while (temp->next)
            {
                showStu(temp->next);
                temp = temp->next;
            }
        }
        else
        {
            if (a == 1)
            {
                printf("id:");
                scanf("%d", &a);
                if (a == 0)
                {
                    printf("\n0 is not an legal ID.");
                    break;
                }
                temp = findStu(m_head, a,NULL );
                if (temp == NULL)
                {
                    printf("\nNo such a student");
                    break;
                }
                showTitle();
                showStu(temp->next);
            }
            else
            {
                if (a == 2)
                {
                    printf("name:");
                    fflush(stdin);
                    gets(name);
                    temp = findStu(m_head, 0, name);
                    if (temp == NULL)
                    {
                        printf("\nNo such a student");
                        break;
                    }
                    showTitle();
                    showStu(temp->next);
                }
            }
        }
        break;
    case 2:
        create();
        break;
    case 3:
        if (m_head == NULL || m_head->next == NULL)
        {
            printf("\nNo student data.");
            break;
        }
        temp = m_head;
        printf("\ninput 1 and then an id or 2 and then a name to delete:");
        scanf("%d", &a);
        if (a == 1)
        {
            printf("id:");
            scanf("%d", &a);
            if (a == 0)
            {
                printf("\n0 is not an legal ID.");
                break;
            }
            deleteStu(findStu(m_head, a, NULL));
        }
        else
        {
            if (a == 2)
            {
                printf("name:");
                fflush(stdin);
                gets(name);
                deleteStu(findStu(m_head, 0, name));
            }
        }
        break;
    case 4:
        if (m_head == NULL || m_head->next == NULL)
        {
            printf("\nNo student data.");
            break;
        }
        temp = m_head;
        printf("\ninput 1 and then an id or 2 and then a name to update:");
        scanf("%d", &a);
        if (a == 1)
        {
            printf("id:");
            scanf("%d", &a);
            updateStu(findStu(m_head, a, NULL)->next);
        }
        else
        {
            if (a == 2)
            {
                printf("name:");
                fflush(stdin);
                gets(name);
                updateStu(findStu(m_head, 0, name)->next);
            }
        }
        break;
    case 5:
        if (m_head == NULL || m_head->next == NULL)
        {
            printf("\nNo student data.");
            break;
        }
        var=varianceStu(m_head);
        printf("average:%f,variance:%f,Standard Deviation:%f", m_ave,var, sqrt(var));

        break;
    case 6:
        exit(0);
    }
}

/**
*print function list
*by cantjie
*/
void printFunctionList()
{
    printf("\n_____________________________________");
    printf("\n1.print students' information");
    printf("\n2.add a new student");
    printf("\n3.delete a student");
    printf("\n4.update a student");
    printf("\n5.show the analysis of students' grade");
    printf("\n6.exit");
    printf("\nInput a number to choose function:");
}

/**
*let user to choose function,after printing function list
*by cantjie
*/
void chooseFunction()
{
    int flag = 0;
    printFunctionList();
    while (scanf("%d", &flag))
    {
        if (flag < 1 && flag>6)
        {
            printf("\nError!");
            printFunctionList();
            continue;
        }
        else
        {
            controller(flag);
            printFunctionList();
        }
    }
}



int main()
{
    m_head=readStu();
    chooseFunction();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cantjie/article/details/71244651