【前言:如题,本文中涉及的项目是去年应学妹要求帮忙写的,纯C语言,在Turbo C下编译通过。最近整理资料,觉得该项目中涉及到的结构体、链表、文件操作、断言等基础知识,以及这个小项目的函数设计和变量命名等容易忽略的知识对初学C语言的朋友应该会有帮助,所以决定发布出来。同时,给需要完成类似课程设计的学生一个参考,但是应注意理解其中的知识点,而不应复制粘贴草草了事。】
问题描述:
学期考试结束,统计某班每个学生的平均成绩,每门课的平均成绩,并按个人平均成绩从高到低的顺序输出成绩,输出不及格人名单。输入、输出格式自定。
实现提示:
考试课程有:高等数学、物理、外语、C语言4门课程。录入所有同学的成绩,对数据进行处理,输出所要求的内容,程序的功能主要包括以下几个方面:
① 输入成绩
② 修改记录
③ 删除记录
④ 输出成绩并按平均成绩排序,并标记平均分不及格的学生。
⑤ 界面提供上述功能选择。
⑥ 学生人数由软件根据输入的成绩记录数自动控制。
⑦ 提供输出成绩到文件以及从文件读取成绩功能。
测试数据:(自定模拟数据如下)
整体设计
通过问题描述和对软件功能需求的分析,我们对程序的设计作出整体构思,其关键点在于数据结构的设计以及对数据的保存。本文提出一种用线性表和文件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;
算法
程序多处需要多链表进行查询和排序,为方便起见,本文使用遍历算法进行查询,使用冒泡法进行排序。详见附录。
文件操作
包括文件内容的读取和写入。为方便用户直接在文件中添加学生信息、查看程序对数据处理后的结果以及便于演示,我们并没有以二进制方式操作数据,而是将数据以文本方式写入文件,因此增加了对字符串操作的代码。详见附录。
程序流程图
部分操作及细节说明
1、该系统提供五个选项,根据提示操作即可。
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");
}