十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

学生管理系统可能是计算机相关专业学生在学完C语言和C++之后能做的比较完整的一个程序, 所以问答中经常出现. 例如设计学生成绩管理系统.
用C语言或C++的基本特性就完全能够解决, 基本用不到多线程, 多态, 模板等.
但有个小问题, 在Windows平台和Linux平台, 对中文的编码和输出不太一样, Windows终端是GBK编码, 虽然可以用简单命令转为UTF-8编码, 但实践操作中发现, 这只适用于汉字输出, 对于输入是一塌糊度. 如果是纯Windows平台程序, 我倒是建议将代码编码格式直接设置为GBK, 这样对于输入输出不会有什么问题, 只不过会有一些编码警告, 忽略即可.
如果要跨平台, 甚至日后进行拓展, 则需要UTF-8编码, 而对编码的转换, 需要一些函数实现, 目前问题主要集中在Windows中.
可能是某些历史原因, Windows终端选择用GBK编码, 而Linux终端则选择用utf8编码, 这导致了跨平台IO的不同, 一套代码不能在两个平台使用.
以前我们讨论过, 关于中文编码, 存储, 编码转换, 多字节, 宽字节等等, 总之到处是坑, 防不胜防, 不亲自踩几个坑, 很难知道这水有多混.
由于Linux终端编码如此贴心, 无需太多顾忌, 我们只需处理号Windows终端编码就好了.
我们需要判断目前程序是在哪个平台, 于是要用到宏:
#ifdef __WIN64
SetConsoleOutputCP(CP_UTF8); // 终端输出的是UTF8编码
SetConsoleCP(CP_ACP); // 终端输入的是GBK编码
#endif以上代码的意思是如果定义了win64, 也就是在Windows64平台, 那么设置输出编码是UTF8, 输入编码是系统的local编码, 也就是中文GBK. 当然以上编码需要windows.h头文件, 这个会在下面程序代码中.
为什么不把输入编码设为UTF8, 和Linux一样, 这样就省事多了. 开始我也是这么想的, 但是马上就踩坑了, 当输入设置为utf8, 汉字无法输入, 我不知到它究竟将汉字转换成什么东西, 总之是不可以, 这个终端黑盒没有透露什么细节, 所以没办法.
退而求其次, 用终端local编码进行输入, 因为我们的程序编码是utf8, 输入进来的是GBK, 中间没有转化, 所以如果直接看, 就是乱码, 直接输出也是乱码.
为了补足编码转换, 我用了下面这个Windows专有的转换函数. 通过终端传过来的四不像编码文字转换为宽字符( MultiByteToWideChar() ), 再将宽字符转回真正的utf8编码文字( WideCharToMultiByte() ).
细节上, 由于名字很可能有空格, 所以需要读取整行, 但是通常如果直接读会读到上一个输入剩下的换行符, 完美错过真正的内容, 所以需要进行输入流清空( ignore() ). 同时为防止姓名字过多, 一般是故意的, 但某些国家名字百八十个字母也是有的, 需要在超出缓冲区后清空输入流, 以防止后续直接跳出.
为了节省点创建缓冲区时间, 我把缓冲区设置为静态区域, 但要注意, 静态区域在你用完后不会自己清空, 所以需要手动清空,以备后用.
#ifdef __WIN64
#includevoid gbkToUtf8(std::istream &istm, char *chrs)
{static char temp[BUFFERSIZE] = {};
static wchar_t wTemp[WCHARBUFFERSIZE] = {};
istm.clear();
istm.ignore(CLEARCHARSIZE, '\n');
if (!istm.getline(temp, BUFFERSIZE))
{istm.clear();
istm.ignore(CLEARCHARSIZE, '\n');
}
MultiByteToWideChar(CP_ACP, 0, temp, -1, wTemp, WCHARBUFFERSIZE);
WideCharToMultiByte(CP_UTF8, 0, wTemp, -1, chrs, BUFFERSIZE, nullptr,
nullptr);
memset(reinterpret_cast(temp), 0, BUFFERSIZE);
memset(reinterpret_cast(wTemp), 0, BUFFERSIZE);
}
#endif
二、完整代码这是基本要求, 不难实现:
设计和实现一个“学生成绩管理系统”,满足以下要求:
为了满足二进制存储和读取, 学生类不能用string或vector这种非平凡类对象成员, 这样只能用char数组表示名字的字符串, 但为了进行方便比较, 还是实现一个函数将字符串转换为string.
为了便于添加和排序以及查找, 我用list链表存储学生数据. 由于各成员函数使用了大量算法库中的函数, 所以也大量引入了简单的lambda闭包, 如果你对lambda不熟悉, 请务必尽快熟悉, 否则C++高级性质就基本与你无缘. 另外, 某些算法函数会再套用算法函数, 不必害怕, 都非常简单, 相比于自己实现, 这种函数套用更为方便, 且语义更为准确.
菜单实现选择太多, 就不要用if语句了, swich语句是最为合适的.
#include
#include#include#include#include#include#define BUFFERSIZE 32
#define WCHARBUFFERSIZE 16
#define CLEARCHARSIZE 1024
#ifdef __WIN64
#includevoid gbkToUtf8(std::istream &istm, char *chrs)
{static char temp[BUFFERSIZE] = {};
static wchar_t wTemp[WCHARBUFFERSIZE] = {};
istm.clear();
istm.ignore(CLEARCHARSIZE, '\n');
if (!istm.getline(temp, BUFFERSIZE))
{istm.clear();
istm.ignore(CLEARCHARSIZE, '\n');
}
MultiByteToWideChar(CP_ACP, 0, temp, -1, wTemp, WCHARBUFFERSIZE);
WideCharToMultiByte(CP_UTF8, 0, wTemp, -1, chrs, BUFFERSIZE, nullptr,
nullptr);
memset(reinterpret_cast(temp), 0, BUFFERSIZE);
memset(reinterpret_cast(wTemp), 0, BUFFERSIZE);
}
#endif
struct student
{student() = default;
explicit student(std::istream &istm)
{std::cout<< "输入学号: "<< std::endl;
istm >>xueHao;
std::cout<< "输入姓名"<< std::endl;
#ifdef __WIN64
gbkToUtf8(istm, xingMing);
#endif
#ifndef __WIN64
istm >>xingMing;
#endif
std::cout<< "输入计算机成绩"<< std::endl;
istm >>jiSuanJiChengJi;
std::cout<< "输入数学成绩"<< std::endl;
istm >>shuXueChengJi;
std::cout<< "输入物理成绩"<< std::endl;
istm >>wuLiChengJi;
std::cout<< "输入外语成绩"<< std::endl;
istm >>waiYuChengJi;
zongFen = jiSuanJiChengJi + shuXueChengJi + wuLiChengJi + waiYuChengJi;
}
[[nodiscard]] auto showZongFen() const ->int
{return zongFen;
}
[[nodiscard]] auto showXueHao() const ->int
{return xueHao;
}
[[nodiscard]] auto showXingMing() const ->std::string
{return xingMing;
}
void print() const
{std::cout<< xueHao<< '\t'<< xingMing<< '\t'<< jiSuanJiChengJi
<< '\t'<< shuXueChengJi<< '\t'<< wuLiChengJi<< '\t'
<< waiYuChengJi<< '\t'<< zongFen<< '\n';
}
void printZongFen() const
{std::cout<< xueHao<< '\t'<< xingMing<< '\t'<< zongFen<< '\n';
}
void printTo(std::ostream &ostm) const
{ostm.write(reinterpret_cast(this), sizeof(student));
}
[[nodiscard]] auto everyScoreUp(int num) const ->bool
{return jiSuanJiChengJi >num && shuXueChengJi >num &&
wuLiChengJi >num && waiYuChengJi >num;
}
private:
int xueHao = 0;
char xingMing[BUFFERSIZE] = {};
int jiSuanJiChengJi = 0;
int shuXueChengJi = 0;
int wuLiChengJi = 0;
int waiYuChengJi = 0;
int zongFen = 0;
};
struct studentVector
{studentVector() = default;
void viewStudent()
{std::cout<< "学号\t姓名\t计算机\t数学\t物理\t外语\t总分\n";
std::for_each(studentList.begin(), studentList.end(),
[](const student &stu) {stu.print(); });
}
void viewZongFen()
{std::cout<< "学号\t姓名\t总分\n";
std::for_each(studentList.begin(), studentList.end(),
[](const student &stu) {stu.printZongFen(); });
}
void sortWithXueHao()
{studentList.sort([](const student &stuA, const student &stuB) {return stuA.showXueHao()< stuB.showXueHao();
});
sortXueHao = true;
}
void sortWithZongFen()
{studentList.sort([](const student &stuA, const student &stuB) {return stuA.showZongFen() >stuB.showZongFen();
});
sortXueHao = false;
}
void pushStudent(const student &stu)
{studentList.push_back(stu);
sortXueHao = false;
}
void insertStudent(const student &stu)
{if (sortXueHao)
{studentList.insert(
std::find_if(studentList.begin(), studentList.end(),
[&stu](const student &stuA) { return stu.showXueHao()< stuA.showXueHao();
}),
stu);
}
else
{sortWithXueHao();
studentList.insert(
std::find_if(studentList.begin(), studentList.end(),
[&stu](const student &stuA) { return stu.showXueHao()< stuA.showXueHao();
}),
stu);
}
}
void searchStudentWithXueHao(int xueHao_)
{auto result = std::find_if(studentList.begin(), studentList.end(),
[&xueHao_](const student &stu) { return xueHao_ == stu.showXueHao();
});
if (result != studentList.end())
{result->print();
}
}
void searchStudentWithXingMing(const std::string &xingMing_)
{auto result = std::find_if(studentList.begin(), studentList.end(),
[&xingMing_](const student &stu) { return xingMing_ == stu.showXingMing();
});
if (result != studentList.end())
{result->print();
}
}
void searchStudentWithEveryScoreUp80()
{std::for_each(studentList.begin(), studentList.end(),
[](const student &stu) { if (stu.everyScoreUp(80))
{ stu.print();
}
});
}
auto deleteStudent(int xueHao_) ->bool
{auto result = std::find_if(studentList.begin(), studentList.end(),
[&xueHao_](const student &stu) { return xueHao_ == stu.showXueHao();
});
if (result != studentList.end())
{studentList.erase(result);
return true;
}
return false;
}
void save()
{std::ofstream file("studentVector.bak");
std::for_each(studentList.begin(), studentList.end(),
[&file](const student &stu) {stu.printTo(file); });
}
void read()
{std::ifstream file("studentVector.bak");
student stu;
while (file.read(reinterpret_cast(&stu), sizeof(student)))
{studentList.push_back(stu);
}
}
private:
std::liststudentList;
bool sortXueHao = false;
};
void pritnCaiDan()
{std::cout<< "学生成绩管理系统, 请按相应数字进行操作\n";
std::cout<< "————————————————————命令概览—————————————————————\n";
std::cout<< "| 1 查看学生信息\t\t\t\t|\n";
std::cout<< "| 2 计算学生总分\t\t\t\t|\n";
std::cout<< "| 3 按照学号排序(由小到大)\t\t\t|\n";
std::cout<< "| 4 按照总成绩排序(由高到低)\t\t\t|\n";
std::cout<< "| 5 添加学生\t\t\t\t\t|\n";
std::cout<< "| 6 插入学生\t\t\t\t\t|\n";
std::cout<< "| 7 按学号查找学生信息\t\t\t\t|\n";
std::cout<< "| 8 按姓名查找学生信息\t\t\t\t|\n";
std::cout<< "| 9 查找每门高于80分学生信息\t\t\t|\n";
std::cout<< "| 10 删除指定学号的学生\t\t\t\t|\n";
std::cout<< "| 11 保存到指定文件\t\t\t\t|\n";
std::cout<< "| 12 从指定文件读取数据\t\t\t\t|\n";
std::cout<< "| 15 退出程序\t\t\t\t\t|\n";
std::cout<< "—————————————————————————————————————————————————\n";
}
auto main() ->int
{#ifdef __WIN64
SetConsoleOutputCP(CP_UTF8); // 终端输出的是UTF8编码
SetConsoleCP(CP_ACP); // 终端输入的是GBK编码
#endif
studentVector stuVec;
pritnCaiDan();
int caiDanXuanXiang = 0;
while (std::cin >>caiDanXuanXiang)
{switch (caiDanXuanXiang)
{case 1:
stuVec.viewStudent();
break;
case 2:
stuVec.viewZongFen();
break;
case 3:
stuVec.sortWithXueHao();
break;
case 4:
stuVec.sortWithZongFen();
break;
case 5:
stuVec.pushStudent(student(std::cin));
break;
case 6:
stuVec.insertStudent(student(std::cin));
break;
case 7: {std::cout<< "请输入学号: "<< std::endl;
int xueHao_;
std::cin >>xueHao_;
std::cout<< "学号\t姓名\t计算机\t数学\t物理\t外语\t总分\n";
stuVec.searchStudentWithXueHao(xueHao_);
break;
}
case 8: {std::cout<< "请输入学生姓名: "<< std::endl;
#ifdef __WIN64
static char temp[BUFFERSIZE] = {};
gbkToUtf8(std::cin, temp);
std::cout<< "学号\t姓名\t计算机\t数学\t物理\t外语\t总分\n";
stuVec.searchStudentWithXingMing(temp);
memset(reinterpret_cast(temp), 0, BUFFERSIZE);
break;
#endif
#ifndef __WIN64
std::string xingMing_;
std::cin >>xingMing_;
std::cout<< "学号\t姓名\t计算机\t数学\t物理\t外语\t总分\n";
stuVec.searchStudentWithXingMing(xingMing_);
break;
#endif
}
case 9: {std::cout<< "学号\t姓名\t计算机\t数学\t物理\t外语\t总分\n";
stuVec.searchStudentWithEveryScoreUp80();
break;
}
case 10: {std::cout<< "请输入学号: "<< std::endl;
int xueHao_;
std::cin >>xueHao_;
stuVec.deleteStudent(xueHao_);
break;
}
case 11:
stuVec.save();
break;
case 12:
stuVec.read();
break;
case 15:
return 0;
break;
}
std::cout<< "\n\n";
pritnCaiDan();
}
return 0;
}
学生管理系统, 可以说是最简单, 只需要基础 IO 知识就可以编写的完整有用的程序, 如果不用跨平台, 那是相当简单. 简单程序也可以逐渐扩展, 比如socket编程, 远程调用, 比如连接数据库进行增删改查, 甚至多线程调用, 比如 qt 加图形菜单称为一个界面程序.
路漫漫其修远兮, 吾将上下而求索.
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧