实现简单的学生选课信息管理系统

本文介绍了一项中山大学软件工程课程的大作业,任务是设计并实现一个学生选课信息管理系统,包括学生信息、课程信息及选课信息的增删改查功能。系统采用C语言编写,数据存储在文本文件中,利用链表结构进行数据管理。文章讨论了整体架构、部分实现和业务逻辑,包括结构体设计、文件读写以及菜单系统等。
摘要由CSDN通过智能技术生成

中山大学软件工程程序设计I 大作业。

要求

本系统模拟实现学生课程信息管理系统,其中包括学生信息,课程信息以及学生的选课信息(储存在文本文件当中),其中功能包括三部分:

学生相关功能

  1. 添加学生信息到学生信息文件当中,学生信息包括:学号(stuId),姓名(stuName),性别(stuSex)
  2. 删除学生信息
  3. 改变学生信息
  4. 查看学生信息

课程相关功能

  1. 添加课程信息到课程信息文件中,课程信息包括:课程编号(couId),
    课程名称(couName),课程人数(stuNum),选课人数(curStuNum),平均分(aveScore)
  2. 删除课程信息
  3. 改变课程信息

选课相关功能

  1. 选课,即添加学生到课程,将学生添加到当前课程的选课信息表中,需要保证:
    1. 学生已经添加到学生信息表中;
    2. 添加到已有的课程中,即课程信息表中有当前课程;
    3. 当前课程还可选,即已选课的人数未超过课程的最大人数。
      此时每个课程都对应一张选课信息文件,存储当前课程的选课情况。
      选课信息包括:学生编号(stuId)、成绩(stuScore)默认为0/空
  2. 退选,即将学生从课程中删除。
  3. 成绩录入
  4. 查看选课信息

实现目标

实现学生课程信息管理系统,可以在命令行进行上述功能的操作,最终的信息保存在相应的文件当中。

题解

怕是ACM打多了啥作业都说题解了。。

整体架构

首先我们需要思考程序的主题架构如何。也就类似作文提纲一样的东西。我们需要提炼业务需求,将程序划分成几个模块然后分别实现。这样不仅能使我们的思路清晰,还方便了我们的实现(因为逻辑混在一起一旦出错很难调试,而且后期如果要扩展也会造成困难,一堆绳子缠在一起自然难解开,我们需要先捋顺绳子)。

通过读题,我们发现题目给出了3大部分,共11个要求。这些要求基本都是增删改查系列数据库的经典操作(当然这里不给用数据库了。。)。我们对于每一个要求,实现一个具体的函数表达相关的功能。比如建立函数addStudent表示添加学生信息操作的功能等等。你可以参考下面的程序看到会有哪些函数。

此外,我们注意到我们还需要将信息存储在文件中,因此很自然地想到我们实际上还有两个需求,分别是从文件中读取信息和将信息存储在文件中。因此最后我们有13个函数,表达我们的业务逻辑(这里的业务逻辑我认为是和数据打交道的意思,这13个函数都是在操作维护学生课程信息,也就是一堆数据)。

然后我们还有绘制界面的函数,一共有4个菜单,分别是主菜单、学生相关功能菜单、课程相关功能菜单和选课相关功能菜单,每个菜单一个函数去绘制界面。比如主菜单应该有4个选项,分别是进入学生菜单、进入课程菜单、进入选课菜单和退出程序。这里要注意我们必须要有退出程序的菜单,以及子菜单必须有返回上一级的选项,作为菜单导航。否则进了程序不能正常退出(按Ctrl-C可以强制结束程序,但我们需要保存信息到文件中,强制退出会导致不保存信息,即丢失信息),进了子菜单不能返回上一级菜单就太滑稽了。

最后我们的主程序,即main函数应该做的事情就是加载数据、控制菜单逻辑和保存数据。要显示那个菜单显然不是菜单自己决定的,我们需要有一个“菜单管理器”去管理菜单,就像文件管理器一样。

部分实现

具体实现细节请参考代码。

main函数和菜单函数

我们在整体架构中已经提了主程序的实现方法,这里我们继续讲。我们给每个菜单标号,这里我们假定主菜单标号为0,学生菜单标号为1,课程菜单标号为2,选课菜单标号为3。如果我们在main函数里设置一个变量loc表示当前显示的菜单标号,然后通过标号调用对应菜单的函数绘制就可以了。注意我们绘制菜单的时候需要清屏(清屏的方法是system("cls"),当然仅适用于Windows操作系统,表示调用控制台的cls命令,这个命令的功能就是清屏)。

也就是说我们main函数实现的代码大概长这样:

loadFrom(FILE); // 加载文件信息
while (1) {
    system("cls"); // 清屏
    switch (loc) {
    case 0: printRootMenu(&loc); break;
    case 1: printSecondaryMenuForStudent(&loc); break;
    case 2: printSecondaryMenuForCourse(&loc); break;
    case 3: printSecondaryMenuForElection(&loc); break;
    case 4: return 0;
}
saveTo(FILE); // 保存文件信息

我们还需要一个特别的标号4表示退出程序。这里我的实现思路是每个菜单函数里先绘制界面,也就是不断地printf,然后scanf读入我们的选项,即选了菜单的哪一项,再判断对应的操作,比如选了添加学生信息就调用addStudent函数开始添加学生信息。最后如果选到了返回上一级菜单,也就是说loc要变了(因为loc表示当前菜单的编号,返回上一级菜单,编号自然不一样),所以我们允许每个菜单函数修改loc,比如loc=0就可以表示回到根菜单,那我们设置loc=4就表示退出程序,一个道理。当然有大佬会说你这么写很不好,确实,菜单是树形结构,如果遇到多级菜单,我们就需要记录每个菜单的父菜单是哪一个,然后返回上一级菜单就将loc=parent[loc]即可。

最后文件的管理我们通过fopen函数获取一个FILE*指针,然后传给loadFrom函数和saveTo函数使用。记得用完后调用fclose关闭文件。

业务逻辑

到了处理业务的时候了。我们这里着重讲学生信息的增删,分别对应addStudentremoveStudent

结构体

首先我们需要Student结构体描述一个学生的信息:

struct Student {
    int id;
    char name[20];
    int sex;
};

分别表示学号、名字和性别。课程信息和选课信息的结构体请参考程序。

addStudent

提示用户输入

显然我们需要添加学生的话,就需要用户输入学生id、名字和性别。
所以我们最开始需要提示用户要输入什么,所以:

printf("Please enter student id first, name second and sex later with 0 meaning male, 1 meaning female.\n");

然后要求用户连着输入id、name和sex。当然我们可以这样:

printf("Please enter student id: "); scanf("%d", &student.id);
printf("Please enter student name: "); scanf("%s", student.name);
printf("Please enter student sex with 0 male and 1 female: "); scanf("%d", &student.sex);

输入性别这方面,我们可以要求用户输入malefemale,不过这可能带来更大的代码量,而且用户可能输入错误,因此我们只允许用户输入01表示男和女。然后对于用户的输入,我们一定要注意验证用户输入的合法性,我们不能保证用户输入的东西都是正确的,用户可以在输入性别的时候输入不是0和1的数字比如2,更可以是字符串。对于用户输入了错误数字的情况,我们读取了sex以后要判断如果sex<0||sex>1直接提示用户输入错误,然后不再添加该学生信息(因为信息是错误的,我们要保证sex是正确的数值)。如果用户输入的不是数字怎么办?这时候我们就需要利用scanf返回值了,scanf返回值表示输入成功的参数个数,比如scanf("%d", &sex),如果用户输入的是字符串,那么scanf返回值就为0,否则为1表示成功输入了一个参数sex,当然还有返回值-1表示文件结束不可以再读入东西了。
因此光用户输入这里我们需要注意的就很多。

数据操作

如果我们使用数组存放学生信息,比如Student stu[1000];这样,那么很简单,stu[++stuCount]=s即可(其中Student s为新学生信息)。

不过我这里使用了链表,大概就是pushFront(&head, &count, &s, sizeof(s))这样。链表的实现细节后面再讲。

removeStudent

删除学生信息,首先我们需要用户输入我们要删除学生的名字,然后我们找到这个学生删除就可以了。也就是说我们先提示用户输入学生名,然后我们再输入字符串。然后还有用户输入的学生不存在的情况,我们要判断一下,并提示学生不存在。

数据维护

我们的数据维护有两种方式,一个是数组存储,一个是链表存储。
数组存储的好处是实现简单,但是如果数组长度不够的时候需要调用realloc函数重新申请内存,当然这消耗的时间其实也不会很多。链表存储的好处是删除快而且自由,但是好像我还没有找到好的方法抽象链表的API。。

链表结构

数组实现没啥好说的,我们这里就讲讲链表吧。
首先我们需要一个能存储任意类型的链表。这需要所谓的“泛型指针”的void*。我们定义链表节点的结构体为

struct Node {
    void *data;
    Node *next, *prev; // 我的代码是双向链表(虽然代码中不需要双向)
};

链表是啥。。建议另外上网查了。如果说数组在内存中的存储是连续的(这样我们访问数组元素只要再内存中直接定位就可以了),那么链表就是不连续存储的(这样我们就需要一个next域表示下一个节点的内存位置)。

插入节点

那么向链表头插入元素的代码是:

void pushFront(Node **head, int *count, const void *new_data, size_t size) {
    ++*count; // 元素个数加一
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = malloc(size); // 复制新数据到新节点里,这么做的好处是我们可以保证无论new_data是不是临时变量,函数都可以正常工作
    memcpy(newNode->data, new_data, size); // 可以试一试将这两行改成newNode->data = new_data; 试试程序会发生什么。
    newNode->next = *head; // 维护链表链接
    newNode->prev = NULL;
    if (*head != NULL) (*head)->prev = newNode;
    *head = newNode;
}

因为我们表示任意类型只有void*这个类型,还是个指针(当然实际上也只能是指针),因此我们在使用的时候必须适应指针,需要另外申请空间存放数据。也就是malloc(size)的作用。至于为什么不是直接用new_data,是因为我想简化程序实现,如果我们直接用new_data,那么我们在调用pushFront时传入的new_data就不能是不持久的变量的地址(比如函数的局部变量在退出函数后就从内存中“消失”了,也就是不持久,这样如果我们持有一个不持久变量的指针,一旦变量从内存中消失了后我们再访问这块内存空间会发生什么呢&

学生选课管理系统 --意义-- 学生选课系统是一个学校不可缺少的部分,它的内容对于学校的决策者和管 理者来说都至关重要,所以学生选课系统应该能够为用户提供充足的信息和快捷 的查询手段。但一直以来人们使用传统人工的方式管理文件档案,这种管理方式 存在着许多缺点,如:效率低、保密性差,另外时间一长,将产生大量的文件和数据, 这对于查找、更新和维护都带来了不少的困难。 随着科学技术的不断提高,计算机科学日渐成熟,其强大的功能已为人们深 刻认识,它已入人类社会的各个领域并发挥着越来越重要的作用。 作为计算机应用的一部分,使用计算机对选课信息管理,有着手工管理 所无法比拟的优点.例如:检索迅速、查找方便、可靠性高、 存储量大、保密性好、 寿命长、成本低等。这些优点能够极大地提高人事劳资管理的效率,也是企业的 科学化、正规化管理,与世界接轨的重要条件。 --课题的可行性分析-- (1) 技术可行性-----本系统仅需要一台装有 Windows/Linux 系统并装有 Mysql数据库和Tomcat服务器的计算机即可,对机器本身没有太高的要求,一般当前学校或个人电脑完全可满足要求。对于软件技术要求,现在的JAVA程序设计语言已非常成熟,本系统采用基于B/S 架构的JavaEE的WEB开发,页面采用CSS样式,Jsp,Javascript等页面技术。 (2) 经济可行性-----由于本系统是一个基于JavaEE的WEB应用,所以服务器端只需部署一台能运行JAVA程序的服务器软件(Tomcat),即可使用本系统,系统成本主要集中在系统软件的开发上,当系统投入运行后可以为学校节约大量的人力,物力。所带来的效益远远大于系统软件的开发成本。在经济上完全可行。 (3) 操作可行性-----界面设计时充分考虑管理人员的习惯,使得操作简单;数据录入迅速、规范、可靠;统计准确;适应力强;容易扩充。 --使用功能模块说明-- (1)登录模块 通过列表框可以分别选择学生、教师和管理员三种用户类型,根据不同的用户类型,登录后会入不同的管理模块 (2)系统管理员模块 ● 选课时间设置 系统管理员通过此项功能设置选课时间段—预选时间段和补选时间段,只有在选课时间段里,学生才可以选择课程,不在此时间段,学生不能选课操作。 ● 选修课程管理 管理员可以行添加新的选修课、删除选修课、修改选修课和查找选修课等操作 添加新的选修课 在选课之前,管理员可以添加新的选修课,让学生选课期间行选修 删除选修课 在选课之前,管理员可以取消某门选修课 修改选修课 在选课之前,管理员可以修改某门选修课的信息 查找选修课 可以根据教师的员工号精确查找该教师所代选修课,也可以根据教师名称和课程名模糊查询选修课,不输入查询条件可以查询所有选修课。针对查询出的某门选修课,可以查看代课老师的信息和选修该课程的所有学生信息。在查看学生信息页面可以修改学生的课程成绩 ● 密码管理 可以对学生、教师和管理员的密码行修改,通过单选框确定要修改密码的用户类型 (3)教师模块 ● 查看所代课程 教师用户登陆系统后,可以查看所代课程的列表,可以查看某门课程的全体学生的信息并能录入学生的成绩,成绩可分批录入,录入后可显示提交按钮,学生成绩的提交,提交之前必须确认填写的学生成绩无误,否则提交之后教师就无法修改学生的成绩,必须通过管理行修改学生的成绩 ● 修改密码 教师可以修改自己的密码 (4)学生选课模块 ● 选择选修课程 学生登陆系统后,在所有选修课程中可以选择两门选修课。只有在选课时间段内才可以使用该功能。在预选阶段,该功能完成选修课程的预选,学生可以选择所有选修课程,预选结束之后,系统开始正选。对超过XX课程计划招生人数的课程,系统自动行抽签,确定哪些学生选课成功,哪些学生选课没有成功。选课不成功的可以行补选。在补选阶段,该功能完成选修课程的补选,补选采用竞争机制,系统会罗列出所有未满的课程,让学生行补选。 ● 查看学分 本功能完成学生对以前选修课程的成绩和所获学分的查看 ● 修改密码 学生可以修改自己的密码 ● 查看已选课选课开始之后,学生可以通过查看已选课程,了解本学期所选的选修课的信息
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值