注:该项目为图形编程。采用C++面向对象程序设计
一.环境准备
开发环境:Visual Studio 2019,easyx图形库
。
easyx下载官网:
easyx使用文档:
EasyX 文档 – 函数说明
https://docs.easyx.cn/zh-cn/reference
二.系统功能列表
功能列表:
- 查看所有学生信息
- 添加学生信息
- 删除学生信息
- 查找某学生,显示其全部信息
- 修改学生信息
- 对学生成绩总和进行排序
- 退出程序存档
- 按Esc键可以随时返回封面
三.程序分解阐释
因为学生成绩管理系统需要处理大量学生的信息,所以采用了
面向对象程序设计
,设计了两个类,
student类用来定义学生的个人信息,还定义了Management类公有继承于student类,Management类的成员函数可以访问student类中的成员变量。
在Management类中定义了一个vector数组,用来存储学生信息。Management类中一共定义了八个成员函数。
1.student类的定义
#pragma once
#include<string>
class student
{
public:
student();
student(const std::string& name, const std::string& gender, int Class, int StuNum, int math, int English, int program);
std::string f();
void display(int m);
std::string name, gender; //定义学生信息有关变量
int Class, StuNum, math,English,program;
};
2.Management类的定义
#pragma once
#include "student.h"
#include<vector>
#include<string>
using namespace std;
class Management :
public student //公有继承,可以访问student内的公有成员
{
public:
Management();
void displayall(); //展示全部
void add(); //添加学生信息
void Erase(); //删除学生信息
void modify(); //修改学生信息
void find(); //查找学生信息
void Sort(); //学生成绩排序
void readData(const string& fileName, char a[]);
void writeData(const string& fileName);
vector<student>Stu; //定义一个student类型的动态数组,大小不受限制
string TableHeader;
string tablebody;
};
3
.student类成员函数的类外定义
#include "student.h"
#include<graphics.h>
#include<iostream>
#include<sstream>
#include<string>
#include<iomanip>
using namespace std;
student::student() :Class(0), StuNum(0), math(0), English(0), program(0)
{
}
student::student( const std::string& name, const std::string& gender,int Class,int StuNum,int math, int English, int program)
:name(name),gender(gender),Class(Class),StuNum(StuNum),math(math),English(English), program(program)
{
}
string student::f() //将int型变量转化为字符数组并写入文件
{
stringstream ss;
ss << this->name <<'\t'<< this->gender <<'\t'<< this->Class <<'\t'<< this->StuNum <<'\t'<< this->math <<'\t'<<this->English <<'\t'<< this->program << endl;
return ss.str();
}
int y = 30;
void student::display(int m)
{
stringstream ss;
ss<<Class<<" "<<StuNum<<setw(6)<<math<<setw(7)<<English<<setw(8)<<program<<setw(10)<<math+English+program;
string tablebody=ss.str(); //将表体数字转化为字符串
outtextxy(5, y, name.c_str());
outtextxy(105, y, gender.c_str()); //输出表
outtextxy(190, y, tablebody.c_str());
y += 30;
if (y == (m+2)*30)
{ //m是目前表中学生数(Stu.size()),该循环使每次输出表后y值重归于30,下次还在原位置输出表
y = 30;
}
}
4.Management类成员函数的定义(逐个说明)
(1)文件写入函数
应用了二进制文件的写入操作,包含在文件
#include<fstream>
中,把数据读入到文档中的文本文件,其他成员函数操作后都要调用文件写入函数来实现存档 的功能。
//写入文件数据函数
void Management::writeData(const string& fileName)
{
fstream write(fileName.c_str(), ios::out);
if (!write.is_open())
{
cerr << " file open failed" << endl; //异常处理
return;
}
TableHeader += '\n';
write.write(TableHeader.c_str(), TableHeader.size()); //写入文件
for (size_t i=0;i<Stu.size();i++)
{
string inf = Stu[i].f();
write.write(inf.c_str(), inf.size());
}
write.close();
}
(2)文件读取函数
采用了二进制文件的读取操作,其中应用了getline读取字符串空格的功能,采用了C语言的memset创建初始值为0的数组,采用了stringstream流把整型数转换为字符数组,将文本文件中的信息读取。
//读取文件数据函数
void Management::readData(const string& fileName,char a[])
{
fstream read(fileName.c_str(), ios::in); //创建输入流对象
if (!read.is_open())
{
cerr << " file open failed" << endl; //异常处理
return;
}
//读取表头
read.getline(buf, 1024); //读汉字
TableHeader = buf;
//读取数据
while (!read.eof())
{
memset(a, 0, sizeof(a));
read.getline(a, 1024);
if (strlen(a) == 0)
break;
stringstream ss(a); //数字变为字符数组,包含在头文件#include<sstream>中
ss >>stu.name>>stu.gender>>stu.Class >> stu.StuNum >> stu.math >> stu.English >> stu.program;
tablebody =ss.str();
Stu.push_back(stu); //将信息存入动态数组
}
read.close(); //关闭
}
(3)显示全部信息函数
先将存入数组的信息都转化为字符数组,然后应用EasyX图形库的outtextxy函数将学生信息输出到图形界面。
//显示全部数据函数
void Management::displayall()
{
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
outtextxy(5, 10,buf);
for (auto&i:Stu)
{
i.display(Stu.size()-1); //传入数组中学生个数-1,因为display()中为m+2,此处传入
Stu.size()-1
}
}
(4)添加学生信息函数
应用了InputBox弹出输入窗口,将所要添加学生的全部信息输入到一个字符数组中,然后写入文本文件。
//添加学生信息函数
void Management::add()
{
char stud[100];
InputBox(stud, 100, "请依次输入姓名,性别,班级,学号,数学成绩,英语成绩,程序设计成绩:");
fstream output("Grade.txt", ios::out | ios::ate|ios::app); //接着文件尾写入
output.write(stud, 30);
output << '\n';
output.close();
}
(5)查找学生信息函数
遍历数组中学生姓名,若与输入姓名相同,则输出该学生所有信息,若不同,则显示未查找到该学生信息。
//查找学生信息函数
void Management::find()
{
char aim[30];
InputBox(aim, 7, "请输入要查找学生的姓名:");
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
int ap = 1; //标记量
for (size_t j=0;j<Stu.size();j++)
{
if (Stu[j].name == aim)
{
outtextxy(5, 10, buf); //查找成功后将该生信息展示
Stu[j].display(0);
ap = 2;
break;
}
else if(j == Stu.size() - 1 && ap == 1)
{
outtextxy(250, 300, "未查找到此人信息");
break;
}
}
}
(6)删除学生信息函数
输入想删除的学生的姓名,进行查找,若找到该学生,调用Vector的函数erase(),将该学生信息删除,若未查找到该学生的名字,则显示未查找到该学生信息。
//删除学生信息函数
void Management::Erase()
{
int po = 1;
char dis[30];
InputBox(dis, 7, "请输入要删除学生的姓名");
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
for (size_t k=0;k<Stu.size();k++)
{
if (Stu[k].name==dis)
{
po = 2;
Stu.erase(Stu.begin()+k,Stu.begin()+k+1); //查找成功,从vector中删除该值
displayall();
writeData("Grade.txt");
break;
}
else if(k==Stu.size()-1&&po==1)
{
outtextxy(100, 200, "未找到此人信息");
}
}
}
(7)修改学生信息函数
同理,先输入要修改信息的学生姓名,再进行查找,如果查找成功,在该学生各项信息下面都有修改按钮,点击修改按钮,输入修改后的信息,退回封面,自动保存。
//修改学生信息函数
void Management::modify()
{
int flag = 1;
char old[30];
char New[50];
InputBox(old, 7, "请输入要修改学生的姓名:");
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
setfillcolor(LIGHTGRAY);
for (size_t i = 0; i < Stu.size(); i++)
{
if (Stu[i].name == old)
{
outtextxy(5, 10, buf);
Stu[i].display(0);
fillrectangle(0, 60, 60, 90);
fillrectangle(80, 60, 140, 90);
fillrectangle(160, 60, 220, 90);
fillrectangle(260, 60, 320, 90);
fillrectangle(360, 60, 410, 90);
fillrectangle(430, 60, 480, 90);
fillrectangle(520, 60, 570, 90);
fillrectangle(200, 300, 500, 350);
outtextxy(10, 65, "修改");
outtextxy(90, 65, "修改");
outtextxy(170, 65, "修改");
outtextxy(270, 65, "修改");
outtextxy(365, 65, "修改");
outtextxy(435, 65, "修改");
outtextxy(525, 65, "修改");
outtextxy(255, 315, "按两次Esc键返回封面");
ExMessage Ms;
int op = 1;
do
{
flag = 2;
Ms = getmessage(EM_MOUSE|EM_KEY);
switch (Ms.message)
{
case WM_LBUTTONDOWN:
if (Ms.x > 0 && Ms.x < 60 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的姓名:");
Stu[i].name = New;
break;
}
if (Ms.x > 80 && Ms.x < 140 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的性别:");
Stu[i].gender = New;
break;
}
if (Ms.x > 160 && Ms.x < 220 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的班级:");
Stu[i].Class = atoi(New);
break;
}
if (Ms.x > 260 && Ms.x < 320 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的学号:");
Stu[i].StuNum = atoi(New); //atoi()定义在#include<cstdlib>文件内,可将字符数组转化为int
break;
}
if (Ms.x > 360 && Ms.x < 410 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的数学成绩:");
Stu[i].math = atoi(New);
break;
}
if (Ms.x > 430 && Ms.x < 480 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的英语成绩:");
Stu[i].English = atoi(New);
break;
}
if (Ms.x > 520 && Ms.x < 570 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的程序设计成绩:");
Stu[i].program = atoi(New);
break;
}
case WM_KEYDOWN:
if (Ms.vkcode = VK_ESCAPE) //按Esc键返回封面
{
op = 0;
break;
}
}
if (op == 0) break; //跳出while循环
} while (1);
}
if (i == (Stu.size()-1) && flag == 1)
{
outtextxy(250, 300, "未查找到该学生信息");
fillrectangle(200, 400, 500, 450);
outtextxy(255, 415, "按两次Esc键返回封面");
break;
}
}
writeData("Grade.txt"); //保存修改数据
}
(8)成绩排序函数
因为动态数组中存储的都是类,所以要预先定义好比较的变量,
这里定义了一个小函数cmp,形式是两个对象的成绩总和进行比较,然后再调用STL容器中的sort()函数
,进行对成绩总和的排序。
bool cmp(student a, student b) //定义排序方式
{
return a.math+a.English+a.program > b.math+b.English+b.program;
}
//成绩排序函数
void Management::Sort()
{
sort(Stu.begin(), Stu.end(),cmp); //sort()函数,排序成绩
displayall();
}
6.Management类外定义成员函数(总)
#include "Management.h"
#include<graphics.h>
#include<conio.h>
#include<iostream>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
using namespace std;
char buf[1024] = { 0 };
char a[1024] = { 0 };
string tablebody=" ";
string aimname;
student stu; //创建一个学生对象
//构造函数读数据
Management::Management()
{
readData("Grade.txt",a);
//writeData("./test.txt");
}
//读取文件数据函数
void Management::readData(const string& fileName,char a[])
{
fstream read(fileName.c_str(), ios::in); //创建输入流对象
if (!read.is_open())
{
cerr << " file open failed" << endl; //异常处理
return;
}
//读取表头
read.getline(buf, 1024); //读汉字
TableHeader = buf;
//读取数据
while (!read.eof())
{
memset(a, 0, sizeof(a));
read.getline(a, 1024);
if (strlen(a) == 0)
break;
stringstream ss(a); //数字变为字符数组,包含在头文件#include<sstream>中
ss >>stu.name>>stu.gender>>stu.Class >> stu.StuNum >> stu.math >> stu.English >> stu.program;
tablebody =ss.str();
Stu.push_back(stu); //将信息存入动态数组
}
read.close(); //关闭
}
//写入文件数据函数
void Management::writeData(const string& fileName)
{
fstream write(fileName.c_str(), ios::out);
if (!write.is_open())
{
cerr << " file open failed" << endl; //异常处理
return;
}
TableHeader += '\n';
write.write(TableHeader.c_str(), TableHeader.size()); //写入文件
for (size_t i=0;i<Stu.size();i++)
{
string inf = Stu[i].f();
write.write(inf.c_str(), inf.size());
}
write.close();
}
//显示全部数据函数
void Management::displayall()
{
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
outtextxy(5, 10,buf);
for (auto&i:Stu)
{
i.display(Stu.size()-1); //传入数组中学生个数-1,因为display()中为m+2,此处传入Stu.size()-1
}
}
//添加学生信息函数
void Management::add()
{
char stud[100];
InputBox(stud, 100, "请依次输入姓名,性别,班级,学号,数学成绩,英语成绩,程序设计成绩:");
fstream output("Grade.txt", ios::out | ios::ate|ios::app); //接着文件尾写入
output.write(stud, 30);
output << '\n';
output.close();
}
//查找学生信息函数
void Management::find()
{
char aim[30];
InputBox(aim, 7, "请输入要查找学生的姓名:");
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
int ap = 1; //标记量
for (size_t j=0;j<Stu.size();j++)
{
if (Stu[j].name == aim)
{
outtextxy(5, 10, buf); //查找成功后将该生信息展示
Stu[j].display(0);
ap = 2;
break;
}
else if(j == Stu.size() - 1 && ap == 1)
{
outtextxy(250, 300, "未查找到此人信息");
break;
}
}
}
//删除学生信息函数
void Management::Erase()
{
int po = 1;
char dis[30];
InputBox(dis, 7, "请输入要删除学生的姓名");
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
for (size_t k=0;k<Stu.size();k++)
{
if (Stu[k].name==dis)
{
po = 2;
Stu.erase(Stu.begin()+k,Stu.begin()+k+1); //查找成功,从vector中删除该值
displayall();
writeData("Grade.txt");
break;
}
else if(k==Stu.size()-1&&po==1)
{
outtextxy(100, 200, "未找到此人信息");
}
}
}
//修改学生信息函数
void Management::modify()
{
int flag = 1;
char old[30];
char New[50];
InputBox(old, 7, "请输入要修改学生的姓名:");
loadimage(NULL, "white.jpg", 700, 700);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
settextstyle(20, 0, _T("楷体"), 0, 0, 5, false, false, false);
setfillcolor(LIGHTGRAY);
for (size_t i = 0; i < Stu.size(); i++)
{
if (Stu[i].name == old)
{
outtextxy(5, 10, buf);
Stu[i].display(0);
fillrectangle(0, 60, 60, 90);
fillrectangle(80, 60, 140, 90);
fillrectangle(160, 60, 220, 90);
fillrectangle(260, 60, 320, 90);
fillrectangle(360, 60, 410, 90);
fillrectangle(430, 60, 480, 90);
fillrectangle(520, 60, 570, 90);
fillrectangle(200, 300, 500, 350);
outtextxy(10, 65, "修改");
outtextxy(90, 65, "修改");
outtextxy(170, 65, "修改");
outtextxy(270, 65, "修改");
outtextxy(365, 65, "修改");
outtextxy(435, 65, "修改");
outtextxy(525, 65, "修改");
outtextxy(255, 315, "按两次Esc键返回封面");
ExMessage Ms;
int op = 1;
do
{
flag = 2;
Ms = getmessage(EM_MOUSE|EM_KEY);
switch (Ms.message)
{
case WM_LBUTTONDOWN:
if (Ms.x > 0 && Ms.x < 60 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的姓名:");
Stu[i].name = New;
break;
}
if (Ms.x > 80 && Ms.x < 140 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的性别:");
Stu[i].gender = New;
break;
}
if (Ms.x > 160 && Ms.x < 220 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的班级:");
Stu[i].Class = atoi(New);
break;
}
if (Ms.x > 260 && Ms.x < 320 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的学号:");
Stu[i].StuNum = atoi(New); //atoi()定义在#include<cstdlib>文件内,可将字符数组转化为int
break;
}
if (Ms.x > 360 && Ms.x < 410 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的数学成绩:");
Stu[i].math = atoi(New);
break;
}
if (Ms.x > 430 && Ms.x < 480 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的英语成绩:");
Stu[i].English = atoi(New);
break;
}
if (Ms.x > 520 && Ms.x < 570 && Ms.y>60 && Ms.y < 90)
{
InputBox(New, 10, "请输入修改后的程序设计成绩:");
Stu[i].program = atoi(New);
break;
}
case WM_KEYDOWN:
if (Ms.vkcode = VK_ESCAPE) //按Esc键返回封面
{
op = 0;
break;
}
}
if (op == 0) break; //跳出while循环
} while (1);
}
if (i == (Stu.size()-1) && flag == 1)
{
outtextxy(250, 300, "未查找到该学生信息");
fillrectangle(200, 400, 500, 450);
outtextxy(255, 415, "按两次Esc键返回封面");
break;
}
}
writeData("Grade.txt"); //保存修改数据
}
bool cmp(student a, student b) //定义排序方式
{
return a.math+a.English+a.program > b.math+b.English+b.program;
}
//成绩排序函数
void Management::Sort()
{
sort(Stu.begin(), Stu.end(),cmp); //sort()函数,排序成绩
displayall();
}
7.主函数
#include"Management.h"
#include<graphics.h>
#include<iostream>
#include<fstream>
#include<conio.h>
using namespace std;
void init(); //函数声明,封面设计函数
void Button(); //函数声明,按钮触发函数
int main()
{
initgraph(700, 700); //设置窗口大小
init();
while (1)
{
Button();
}
_getch(); //阻塞函数,防止窗口闪退
closegraph();
return 0;
}
void init()
{
loadimage(NULL, "./cover.jpg",700,700); //加载封面图片
setbkmode(TRANSPARENT);
settextcolor(BLACK); //设置字体颜色
settextstyle(60, 0, _T("楷体"), 0, 0, 5, false, false, false);
outtextxy(110, 80, "学生成绩管理系统");
setfillcolor(RED);
fillrectangle(250, 180, 440, 230);
fillrectangle(250, 230, 440, 280);
fillrectangle(250, 280, 440, 330);
fillrectangle(250, 330, 440, 380); //设计七个按钮
fillrectangle(250, 380, 440, 430);
fillrectangle(250, 430, 440, 480);
fillrectangle(250, 480, 440, 530);
settextstyle(30, 0, _T("宋体"), 0, 0, 5, false, false, false);
outtextxy(285, 190, "查看全部");
outtextxy(285, 240, "添加学生");
outtextxy(285, 290, "删除学生");
outtextxy(285, 340, "查找学生");
outtextxy(285, 390, "修改学生");
outtextxy(285, 440, "成绩排序");
outtextxy(285, 490, "退出系统");
settextstyle(20, 0, _T("楷体"), 0, 0, 50, false, false, false);
outtextxy(580, 105, "(1.0)");
}
void Button()
{
ExMessage msg;
Management m; //定义一个Management对象
msg = getmessage(EM_MOUSE|EM_KEY); //鼠标交互和键盘交互
switch (msg.message)
{
case WM_LBUTTONDOWN:
if (msg.x > 250 && msg.x < 440 && msg.y>180 && msg.y < 230)
{
m.displayall();
break;
}
if (msg.x > 250 && msg.x < 440 && msg.y>230 && msg.y < 280)
{
m.add();
break;
}
if (msg.x > 250 && msg.x < 440 && msg.y>280 && msg.y < 330)
{
m.Erase();
break;
}
if (msg.x > 250 && msg.x < 440 && msg.y>330 && msg.y < 380)
{
m.find();
break;
}
if (msg.x > 250 && msg.x < 440 && msg.y>380 && msg.y < 430)
{
m.modify();
break;
}
if (msg.x > 250 && msg.x < 440 && msg.y>430 && msg.y < 480)
{
m.Sort();
break;
}
if (msg.x > 250 && msg.x < 440 && msg.y>480 && msg.y < 530)
{
m.writeData("Grade.txt"); //保存并退出
exit(0);
break;
}
case WM_KEYDOWN:
if (msg.vkcode = VK_ESCAPE) //按Esc键返回封面
{
init();
break;
}
}
}
鸣谢:参考了b站up主C语言Plus的课