广工课程设计——学生成绩统计系统

【前言:如题,本文中涉及的项目是去年应学妹要求帮忙写的,纯C语言,在Turbo C下编译通过。最近整理资料,觉得该项目中涉及到的结构体、链表、文件操作、断言等基础知识,以及这个小项目的函数设计和变量命名等容易忽略的知识对初学C语言的朋友应该会有帮助,所以决定发布出来。同时,给需要完成类似课程设计的学生一个参考,但是应注意理解其中的知识点,而不应复制粘贴草草了事。】

问题描述:
  学期考试结束,统计某班每个学生的平均成绩,每门课的平均成绩,并按个人平均成绩从高到低的顺序输出成绩,输出不及格人名单。输入、输出格式自定。

实现提示:
  考试课程有:高等数学、物理、外语、C语言4门课程。录入所有同学的成绩,对数据进行处理,输出所要求的内容,程序的功能主要包括以下几个方面:
① 输入成绩
② 修改记录
③ 删除记录
④ 输出成绩并按平均成绩排序,并标记平均分不及格的学生。
⑤ 界面提供上述功能选择。
⑥ 学生人数由软件根据输入的成绩记录数自动控制。
⑦ 提供输出成绩到文件以及从文件读取成绩功能。

测试数据:(自定模拟数据如下)

广工课程设计——学生成绩统计系统_第1张图片

整体设计
  通过问题描述和对软件功能需求的分析,我们对程序的设计作出整体构思,其关键点在于数据结构的设计以及对数据的保存。本文提出一种用线性表和文件IO实现上述需求的解决方法,具体设计如下所述。
数据结构
  本文将线性表设计双向循环链表,其中结点数据域为结构体stInfo。

/// 用户信息
typedef struct student
{
    int  id;        // 学号
    char name[NAMELEN]; // 姓名
    int  AMaths;    // 高数成绩
    int  Physics;   // 物理成绩
    int  Foreign;   // 外语成绩
    int  Clan;  // C语言成绩
    float GPA;  // 平均分
}stInfo;
/// 用户信息结点结构体
typedef struct studentNode
{
    stInfo data;        //
    struct studentNode *prev;   //
    struct studentNode *next;
}stNode;

算法
  程序多处需要多链表进行查询和排序,为方便起见,本文使用遍历算法进行查询,使用冒泡法进行排序。详见附录。

文件操作
  包括文件内容的读取和写入。为方便用户直接在文件中添加学生信息、查看程序对数据处理后的结果以及便于演示,我们并没有以二进制方式操作数据,而是将数据以文本方式写入文件,因此增加了对字符串操作的代码。详见附录。

程序流程图

广工课程设计——学生成绩统计系统_第2张图片

部分操作及细节说明

1、该系统提供五个选项,根据提示操作即可。

广工课程设计——学生成绩统计系统_第3张图片

2、文件操作涉及的两个文件,宏定义如下

// 保存学生各科成绩
#define STUDENTS_FILE "./students.txt"
// 保存成绩处理完成后的结果
#define SCORE_FILE "./GPA.txt"

  正常情况下,数据从文件STUDENTS_FILE和手动输入两种渠道进入链表,经过处理后再由链表流向文件STUDENTS_FILE和文件SCORE_FILE。

3、主程序负责接收用户输入的数据,并传到相应接口函数,由于标准输入(即键盘)是带缓冲的,因此需要添加下面这行代码,用以清空缓冲区。

while(getchar() != '\n') {;}

4、输入ID号和成绩时需注意:系统将ID号范围限定为1000到1999,且不能重复,成绩范围限定为0到100。

接口函数说明
相关宏定义

#define OK      1
#define ERROR   0
#define EXIT    2
#define EXIST   1
#define NOTEXIST 0

系统操作相关函数

/*
 * 检查用户输入的ID是否存在于链表;
 * 存在返回EXIST,不存在返回NOTEXIST。 
*/
int CheckID(stNode *stHead, int id);

/*
 * 检查用户输入的成绩是否合理;
 * 无返回值。
*/
void InputScore(int *score);

/*
 * 初始化系统,创建链表并从STUDENTS_FILE中读取数据插入链表;
 * 初始化成功返回OK,否则返回ERROR。
*/
int InitSystem(void);

/*
 * 等待用户输入结点数据并插入链表;
 * 无返回值。
*/
void InputRecord(void);

/*
 * 等待用户修改结点数据;
 * 无返回值。
*/
void AlterRecord(void);

/*
 * 等待用户删除特定结点;
 * 无返回值。
*/
void DeleteRecord(void);

/*
 * 按系统需求对链表进行处理,并将结果输出;
 * 无返回值。
*/
void OutputRecord(void);

/*
 * 退出系统,保存链表数据,释放内存;
 * 无返回值。
*/
void Exit(void);

链表操作相关函数

/*
 * 初始化一个双循环链表,*stHead为链表头指针;
 * 初始化成功返回OK,否则返回ERROR。
*/
int InitList(stNode **stHead);

/*
 * 显示链表stHead所包含的内容;
 * 无返回值。
*/
void ShowList(stNode *stHead);

/*
 * 将链表stHead复制为cpHead;
 * 复制成功返回链表头指针,否则返回NULL。
*/
stNode *CopyList(stNode *cpHead, stNode *stHead);

/*
 * 将链表stHead的数据保存到文件fp中;
 * 无返回值。
*/
void SaveList(FILE *fp, stNode *stHead);

/*
 * 释放链表stHead所占用的内存;
 * 无返回值。
*/
void FreeList(stNode *stHead);

/*
 * 将以st作为数据域的结点插入到链表stHead的末尾;
 * 插入成功返回OK,否则返回ERROR。
*/
int InsertNode(stNode *stHead, stInfo st);

/*
 * 删除链表stHead上相关ID号的结点;
 * 删除成功返回OK,否则返回ERROR。
*/
int DeleteNode(stNode *stHead, int id);

/*
 * 将链表stHead相关ID号的column字段的成绩修改为score;
 * 修改成功返回OK,否则返回ERROR。
*/
int AlterNode(stNode *stHead, int id, int column, int score);

/*
 * 将链表stHead的结点按平均分降序来排序;
 * 无返回值。
*/
void DescSort(stNode *stHead);

/*
 * 显示链表stHead中平均分不及格的名单;
 * 无返回值。
*/
void ShowFail(stNode *stHead);

附录

/************************************************
 File name : resultsMS.c
 Created  date : 2014-10-25 18:38
 Modified date : 2014-10-27 20:07
 Author : luhuadong
 Email  : [email protected]
 Description : 
************************************************/

#include 
#include 
#include 
#include 

/// 保存学生各科成绩
#define STUDENTS_FILE "./students.txt"
/// 保存成绩处理完成后的结果
#define SCORE_FILE "./GPA.txt"

#define OK  1
#define ERROR   0
#define EXIT    2
#define EXIST   1
#define NOTEXIST 0

/// 用户基本信息数据长度
#define STINFOLEN 80
/// 结构体student中成员name的长度
#define NAMELEN 32

/// 用户信息
typedef struct student
{
    int  id;
    char name[NAMELEN];
    int  AMaths;
    int  Physics;
    int  Foreign;
    int  Clan;
    float GPA;
}stInfo;

/// 用户信息结点结构体
typedef struct studentNode
{
    stInfo data;        //
    struct studentNode *prev;   //
    struct studentNode *next;
}stNode;

stNode *stHead = NULL;
int status = OK;

/* 函数声明 */
int CheckID(stNode *stHead, int id);
void InputScore(int *score);
int InitSystem(void);
void InputRecord(void);
void AlterRecord(void);
void DeleteRecord(void);
void OutputRecord(void);
void Exit(void);

int InitList(stNode **stHead);
void ShowList(stNode *stHead);
stNode *CopyList(stNode *cpHead, stNode *stHead);
void SaveList(FILE *fp, stNode *stHead);
void FreeList(stNode *stHead);
int InsertNode(stNode *stHead, stInfo st);
int DeleteNode(stNode *stHead, int id);
int AlterNode(stNode *stHead, int id, int column, int score);
void DescSort(stNode *stHead);
void ShowFail(stNode *stHead);

/* ******************************************************** */
int main(void)
{
    int mode;   // 模式选择
    // 系统初始化
    if(ERROR == InitSystem())
    {
        perror("Initialized system fail");
        return -1;
    }
    while(OK == status)
    {
        // system("clear");
        printf("++++++++++++++++++++++++++++++\n");
        printf(" 1-输入成绩\n 2-修改记录\n 3-删除记录\n 4-输出成绩\n 5-退出\n");
        printf("++++++++++++++++++++++++++++++\n>>");
        while(scanf("%d", &mode) == 0)
        {
            while(getchar() != '\n') {;}
            printf("error, try again >>\n");
        }
        while(getchar() != '\n') {;}

        switch(mode)
        {
            case 1: InputRecord(); break;
            case 2: AlterRecord();break;
            case 3: DeleteRecord();break;
            case 4: OutputRecord();break;
            case 5: Exit(); break;
            default: break;
        }
    }
    return 0;
}
/* ******************************************************** */


int CheckID(stNode *stHead, int id)
{
    stNode *currNode = stHead->next;
    while(currNode != stHead)
    {
        if(id == currNode->data.id) {return EXIST;}
        currNode = currNode->next;
    }
    return NOTEXIST;
}

int InitSystem(void)
{
    FILE *fp = NULL;
    stInfo st;
    int i=0, j=0, k=0;
    char readbuf[STINFOLEN];        //
    char strStInfo[6][NAMELEN]; //

    if(ERROR == InitList(&stHead))
    {
        printf("Created initial LIST failed.\n");
        status = ERROR;
        return ERROR;
    }
    fp = fopen(STUDENTS_FILE, "r");
    if(NULL == fp)
    {
        printf("Open %s failed.\n", STUDENTS_FILE);
        status = ERROR;
        return ERROR;
    }
    while(NULL != fgets(readbuf, STINFOLEN, fp))
    {
        /// 每一条记录共有6个字段
        for(i=0; i<6; i++)
        {
            while((' ' != readbuf[j]) \
                    && ('\n' != readbuf[j]) \
                    && ('\t' != readbuf[j]))
            {
                strStInfo[i][k++] = readbuf[j++];
            }
            strStInfo[i][k] = '\0';
            j++;    //
            k = 0;
        }
        j = 0;
        st.id       = atoi(strStInfo[0]);
        strcpy(st.name, strStInfo[1]);
        st.AMaths   = atoi(strStInfo[2]);
        st.Physics  = atoi(strStInfo[3]);
        st.Foreign  = atoi(strStInfo[4]);
        st.Clan     = atoi(strStInfo[5]);
        st.GPA      = 0;

        if(ERROR == InsertNode(stHead, st)) { return ERROR;}
    }
    printf("初始化链表==>\n");
    ShowList(stHead);
    fclose(fp);
    return OK;
}

void InputScore(int *score)
{
    scanf("%d", score);
    while(getchar() != '\n') {;}
    while(*score < 0 || *score > 100)
    {
        printf("【成绩范围:0--100】\n>>");
        scanf("%d", score);
        while(getchar() != '\n') {;}
    }
    return ;
}

void InputRecord(void)
{
    stInfo st;
    printf("Input ID: ");
    scanf("%d", &st.id);
    if(st.id > 1999 || st.id < 1000)
    {
        printf("ID范围为1000~1999.\n");
    }
    if(EXIST == CheckID(stHead, st.id))
    {
        printf("ID重复!\n");
        return ;
    }
    while(getchar() != '\n') {;}
    printf("姓名:");
    gets(st.name);
    printf("高数成绩:");
    InputScore(&st.AMaths);
    printf("物理成绩:");
    InputScore(&st.Physics);
    printf("外语成绩:");
    InputScore(&st.Foreign);
    printf("C语言成绩:");
    InputScore(&st.Clan);

    if(OK == InsertNode(stHead, st)) {printf("OK\n");}
    else {printf("Failed\n");}

    return ;
}

void AlterRecord(void)
{
    int id, column, score;
    printf("Input ID: ");
    scanf("%d", &id);

    if(NOTEXIST == CheckID(stHead, id))
    {
        printf("ID不存在!\n");
        return ;
    }
    printf("1-高数\t2-物理\t3-外语\t4-C语言\n修改科目>>");
    scanf("%d", &column);
    printf("修改成绩为:");
    scanf("%d", &score);

    if(OK == AlterNode(stHead, id, column, score)) {printf("OK\n");}
    else {printf("Failed\n");}

    return ;
}

void DeleteRecord(void)
{
    int id;
    printf("Input id: ");
    scanf("%d", &id);

    if(NOTEXIST == CheckID(stHead, id))
    {
        printf("ID不存在!\n");
        return ;
    }
    if(OK == DeleteNode(stHead, id)) {printf("OK\n");}
    else {printf("Failed\n");}

    return ;
}

void OutputRecord(void)
{
    FILE *fp = NULL;
    stNode *cpHead = NULL;

    if(ERROR == InitList(&cpHead))
    {
        printf("Created output LIST failed.\n");
        status = ERROR;
        return ;
    }
    CopyList(cpHead, stHead);

    if(NULL == cpHead)
    {
        printf("Copy failed.\n");
        return ;
    }
    DescSort(cpHead);
    printf("按平均分排序==>\n");
    ShowList(cpHead);
    fp = fopen(SCORE_FILE, "w");
    if(NULL == fp)
    {
        printf("Open %s failed.\n", SCORE_FILE);
        return ;
    }
    printf("不及格学生==>\n");
    ShowFail(cpHead);
    SaveList(fp, cpHead);
    FreeList(cpHead);
    fclose(fp);

    return ;
}

void Exit(void)
{
    FILE *fp = fopen(STUDENTS_FILE, "w");
    if(NULL == fp)
    {
        printf("Open %s failed.\n", STUDENTS_FILE);
        return ;
    }
    SaveList(fp, stHead);
    printf("Save to file ...\n");
    FreeList(stHead);
    fclose(fp);
    printf("successful exit.\n");

    status = EXIT;
    return ;
}

//****************************************************

int InitList(stNode **stHead)
{
    *stHead = (stNode *)malloc(sizeof(stNode));

    if(NULL != *stHead)
    {
        (*stHead)->prev = *stHead;
        (*stHead)->next = *stHead;
        return OK;
    }
    return ERROR;
}

void ShowList(stNode *stHead)
{
    stNode *currNode = NULL;
    assert(NULL != stHead);
    printf("================================================================\n");
    printf("学号\t姓名\t\t高数\t物理\t外语\tC语言\t平均分\n");

    currNode = stHead->next;
    while(currNode != stHead)
    {
        printf("%d\t%-8s\t%d\t%d\t%d\t%d\t%.2f\n", currNode->data.id, \
                currNode->data.name, currNode->data.AMaths, \
                currNode->data.Physics, currNode->data.Foreign, \
                currNode->data.Clan, currNode->data.GPA);
        currNode = currNode->next;
    }
    printf("================================================================\n");
}

stNode *CopyList(stNode *cpHead, stNode *stHead)
{
    stInfo st;
    stNode *currNode = NULL;
    assert(NULL != cpHead && NULL != stHead);

    currNode = stHead->next;
    while(currNode != stHead)
    {
        st = currNode->data;
        st.GPA = (float)(st.AMaths + st.Physics + st.Foreign + st.Clan)/4;
        if(ERROR == InsertNode(cpHead, st)) {return NULL;}
        currNode = currNode->next;
    }
    return cpHead;
}

void SaveList(FILE *fp, stNode *stHead)
{
    stNode *currNode = NULL;
    assert(NULL != fp && NULL != stHead);

    currNode = stHead->next;
    while(currNode != stHead)
    {
        fprintf(fp, "%d %s\t%d %d %d %d %.2f\n", currNode->data.id, \
                currNode->data.name, currNode->data.AMaths, \
                currNode->data.Physics, currNode->data.Foreign, \
                currNode->data.Clan, currNode->data.GPA);
        currNode = currNode->next;
    }
}

void FreeList(stNode *stHead)
{
    /// 释放链表
    stNode *currNode = stHead->next;

    while(currNode != stHead)
    {
        stHead->next = currNode->next;
        free(currNode);
        currNode = stHead->next;;
    }
    //free(currNode);   //
    free(stHead);
}

int InsertNode(stNode *stHead, stInfo st)
{
    stNode *new = NULL;
    assert(NULL != stHead);

    new = (stNode *)malloc(sizeof(stNode));
    if(NULL != new)
    {
        new->data = st;

        new->next = stHead;
        new->prev = stHead->prev;
        stHead->prev->next = new;
        stHead->prev = new;
        return OK;
    }
    return ERROR;
}

int DeleteNode(stNode *stHead, int id)
{
    stNode *currNode = stHead->next;
    stNode *delNode = NULL;
    while(currNode != stHead)
    {
        if(id == currNode->data.id)
        {
            delNode = currNode;
            currNode->prev->next = currNode->next;
            currNode->next->prev = currNode->prev;
            currNode = currNode->prev;
            free(delNode);
            return OK;
        }
        currNode = currNode->next;
    }
    return ERROR;
}

int AlterNode(stNode *stHead, int id, int column, int score)
{
    stNode *currNode = stHead->next;
    stNode *delNode = NULL;
    while(currNode != stHead)
    {
        if(id == currNode->data.id)
        {
            printf("修改前==>\n高数:%d\t物理:%d\t外语:%d\tC语言:%d\n", \
                    currNode->data.AMaths, currNode->data.Physics, \
                    currNode->data.Foreign, currNode->data.Clan);

            if(1 == column) {currNode->data.AMaths = score;}
            else if(2 == column) {currNode->data.Physics = score;}
            else if(3 == column) {currNode->data.Foreign = score;}
            else if(4 == column) {currNode->data.Clan = score;}
            else {return ERROR;}

            printf("修改后==>\n高数:%d\t物理:%d\t外语:%d\tC语言:%d\n", \
                    currNode->data.AMaths, currNode->data.Physics, \
                    currNode->data.Foreign, currNode->data.Clan);
            return OK;
        }
        currNode = currNode->next;
    }
    return ERROR;
}

void DescSort(stNode *stHead)
{
    stInfo tmp;
    stNode *curr1 = stHead->next;
    stNode *curr2 = stHead->next;
    stNode *lastNode = stHead->prev;

    for(curr1 = stHead->next; curr1 != stHead->prev; curr1 = curr1->next, lastNode = lastNode->prev)
    {
        for(curr2 = stHead->next; curr2 != lastNode; curr2 = curr2->next)
        {
            if(curr2->data.GPA < curr2->next->data.GPA)
            {
                tmp = curr2->data;
                curr2->data = curr2->next->data;
                curr2->next->data = tmp;
            }
        }
    }
}

void ShowFail(stNode *stHead)
{
    stNode *currNode = NULL;
    assert(NULL != stHead);
    printf("********************************\n");
    printf("* 学号\t姓名\t\t平均分\n");

    currNode = stHead->prev;
    while(currNode->data.GPA < 60)
    {
        printf("* %d\t%-8s\t%.2f\n", currNode->data.id, \
                currNode->data.name, currNode->data.GPA);
        currNode = currNode->prev;
    }
    printf("********************************\n");

}

你可能感兴趣的:(C语言)