实验六
任务一
contestant.hpp
#pragma once #include <iomanip> #include <iostream> #include <string> struct Contestant { long id; // 学号 std::string name; // 姓名 std::string major; // 专业 int solved; // 解题数 int penalty; // 总罚时 }; // 重载<< // 要求:姓名/专业里不含空白符 inline std::ostream& operator<<(std::ostream& out, const Contestant& c) { out << std::left; out << std::setw(15) << c.id << std::setw(15) << c.name << std::setw(15) << c.major << std::setw(10) << c.solved << std::setw(10) << c.penalty; return out; } // 重载>> inline std::istream& operator>>(std::istream& in, Contestant& c) { in >> c.id >> c.name >> c.major >> c.solved >> c.penalty; return in; }
utils.hpp
#pragma once #include <fstream> #include <iostream> #include <stdexcept> #include <string> #include <vector> #include "contestant.hpp" // ACM 排序规则:先按解题数降序,再按罚时升序 inline bool cmp_by_solve(const Contestant& a, const Contestant& b) { if (a.solved != b.solved) return a.solved > b.solved; return a.penalty < b.penalty; } // 将结果写至任意输出流 inline void write(std::ostream& os, const std::vector<Contestant>& v) { for (const auto& x : v) os << x << '\n'; } // 将结果打印到屏幕 inline void print(const std::vector<Contestant>& v) { write(std::cout, v); } // 将结果保存到文件 inline void save(const std::string& filename, const std::vector<Contestant>& v) { std::ofstream os(filename); if (!os) throw std::runtime_error("fail to open " + filename); write(os, v); } // 从文件读取信息(跳过标题行) inline std::vector<Contestant> load(const std::string& filename) { std::ifstream is(filename); if (!is) throw std::runtime_error("fail to open " + filename); std::string line; std::getline(is, line); // 跳过标题 std::vector<Contestant> v; Contestant t; int seq; while (is >> seq >> t) v.push_back(t); return v; }
task1.cpp
#pragma once #include <fstream> #include <iostream> #include <stdexcept> #include <string> #include <vector> #include "contestant.hpp" // ACM 排序规则:先按解题数降序,再按罚时升序 inline bool cmp_by_solve(const Contestant& a, const Contestant& b) { if (a.solved != b.solved) return a.solved > b.solved; return a.penalty < b.penalty; } // 将结果写至任意输出流 inline void write(std::ostream& os, const std::vector<Contestant>& v) { for (const auto& x : v) os << x << '\n'; } // 将结果打印到屏幕 inline void print(const std::vector<Contestant>& v) { write(std::cout, v); } // 将结果保存到文件 inline void save(const std::string& filename, const std::vector<Contestant>& v) { std::ofstream os(filename); if (!os) throw std::runtime_error("fail to open " + filename); write(os, v); } // 从文件读取信息(跳过标题行) inline std::vector<Contestant> load(const std::string& filename) { std::ifstream is(filename); if (!is) throw std::runtime_error("fail to open " + filename); std::string line; std::getline(is, line); // 跳过标题 std::vector<Contestant> v; Contestant t; int seq; while (is >> seq >> t) v.push_back(t); return v; }
data.txt
序号 学号 姓名 专业 解题数(道) 总罚时(分钟) 1 204942005 Jeny 未来专业1 4 210 2 204942302 Alex 未来专业2 3 180 3 204942059 Bob 未来专业2 5 350 4 204942111 Hellen 未来专业3 2 90 5 204942017 chappie 未来专业4 4 260 6 204942075 Shaw 未来专业5 3 150 7 204942076 Thomas 未来专业6 6 420 8 204942078 Jennie 未来专业7 1 30 9 204942079 Tibby 未来专业7 3 200 10 204942080 Vermont 未来专业8 5 310
data_bad.txt
序号 学号 姓名 专业 解题数(道) 总罚时(分钟) 1 204942005 Jeny 未来专业1 4 210 2 204942302 Alex 未来专业2 3 180 3 204942059 Bob 未来专业2 5 350 4 204942111 Hellen 未来专业3 2 90 5 204942017 chappie 未来专业4 4 260 6 204942075 Shaw 未来专业5 3 150 7 204942076 Thomas 未来专业6 8 204942078 Jennie 未来专业7 1 30 9 204942079 Tibby 未来专业7 3 200 10 204942080 Vermont 未来专业8 5 310
结果

1.
(1)因为std::cout的参数类型是std::ostream,而std::ofstream继承自std::ostream
(2)不用,只要设备以std::ostream&的形式提供输出能力,write()不用改动,可以复用
2.
if (!os) throw std::runtime_error("fail to open " + filename);
if (!is) throw std::runtime_error("fail to open " + filename);
(1)第一个是当输出文件无法打开时,os为失败状态,触发抛异常
第二个是当输入文件无法打开,is为失败状态,触发抛异常
(2)在task.cpp的app()中被捕获
处理方式:打印错误信息到标准错误输出,并return结束app()不再继续之后的流程
try { contestants = load(in_file); std::sort(contestants.begin(), contestants.end(), cmp_by_solve); print(contestants); save(out_file, contestants); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; return; }
3.可以,一致,因为std::sort定义为inline函数
4.
(1)

读入使用in >> c.id >> c.name >> c.major >> c.solved >> c.penalty;,当遇到空白行时会自动跳过,但当遇到有缺失值行时,in >> c.solved 或 in >> c.penalty 会失败,流进入 failbit 状态,导致 is >> seq >> t 整体失败,while 循环终止。
任务二
student.hpp
#pragma once #include <iostream> #include <string> class Student { public: Student() = default; ~Student() = default; const std::string get_major() const; int get_grade() const; friend std::ostream& operator<<(std::ostream& os, const Student& s); friend std::istream& operator>>(std::istream& is, Student& s); private: int id; std::string name; std::string major; int grade; // 0-100 };
stumgr.hpp
#pragma once #include <string> #include <vector> #include "student.hpp" class StuMgr { public: void load(const std::string& file); // 加载数据文件(空格分隔) void sort(); // 排序: 按专业字典序升序、同专业分数降序 void print() const; // 打印到屏幕 void save(const std::string& file) const; // 保存到文件 private: void write(std::ostream &os) const; // 把数据写到任意输出流 private: std::vector<Student> students; };
task2.cpp
#include <iostream> #include <limits> #include <string> #include "stumgr.hpp" const std::string in_file = "./data.txt"; const std::string out_file = "./ans.txt"; void menu() { std::cout << "\n**********简易应用**********\n" "1. 加载文件\n" "2. 排序\n" "3. 打印到屏幕\n" "4. 保存到文件\n" "5. 退出\n" "请选择:"; } void app() { StuMgr mgr; while(true) { menu(); int choice; std::cin >> choice; try { switch (choice) { case 1: mgr.load(in_file); std::cout << "加载成功\n"; break; case 2: mgr.sort(); std::cout << "排序已完成\n"; break; case 3: mgr.print(); std::cout << "打印已完成\n"; break; case 4: mgr.save(out_file); std::cout << "导出成功\n"; break; case 5: return; default: std::cout << "不合法输入\n"; } } catch (const std::exception& e) { std::cout << "Error: " << e.what() << '\n'; } } } int main() { app(); }
data.txt
学号 姓名 专业 成绩 1001 抖森 Acting 80 1002 宝爷 Music 97 1003 大眼仔 Acting 75 1004 本喵 Acting 99 1005 裘花 Acting 89 1006 小李子 Acting 92 1007 无脸男 Acting 85 1008 甜茶 Acting 91 1009 囧瑟夫 Acting 88 1010 霉霉 Music 96
data_bad.txt
学号 姓名 专业 成绩 1001 抖森 Acting 80 1002 宝爷 Music 97 1003 大眼仔 Acting 1004 本喵 Acting 99 1005 裘花 Acting 89 1006 小李子 Acting 92 1007 无脸男 Acting 105 1008 甜茶 Acting 91 1009 囧瑟夫 Acting 88 1010 霉霉 Music 96
student.cpp
#include "student.hpp" const std::string Student::get_major() const { return major; } int Student::get_grade() const { return grade; } std::ostream& operator<<(std::ostream& os, const Student& s) { os << s.id << ' ' << s.name << ' ' << s.major << ' ' << s.grade; return os; } std::istream& operator>>(std::istream& is, Student& s) { is >> s.id >> s.name >> s.major >> s.grade; return is; }
stumgr.cpp
#include <fstream> #include <algorithm> #include <sstream>
#include"stumgr.hpp" void StuMgr::write(std::ostream &os) const // 把数据写到任意输出流 { for (const auto& student : students) { os << student << '\n'; } } void StuMgr::load(const std::string& file) // { std::ifstream is(file); if (!is.is_open()) { throw std::runtime_error("无法打开文件: " + file); } students.clear(); std::string line; std::getline(is, line); Student t; int line_no = 1; while (std::getline(is, line)) { ++line_no; std::istringstream ss(line); if(!(ss >> t)) { std::cerr << "[load warning] bad line " << line_no << ": " << line << '\n'; continue; }else if(t.get_grade() < 0 || t.get_grade() > 100) { std::cerr << "[load warning] invalid grade at line " << line_no << ": " << line << '\n'; continue; } students.push_back(t); } } void StuMgr::sort() { if (students.empty()) { throw std::runtime_error("没有加载任何学生数据,无法排序"); return; } std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) { if (a.get_major() != b.get_major()) { return a.get_major() < b.get_major(); } return a.get_grade() > b.get_grade(); }); } void StuMgr::print() const { if (students.empty()) { throw std::runtime_error("没有加载任何学生数据,无法打印"); return; } write(std::cout); } void StuMgr::save(const std::string& file) const // 保存到文件 { if(students.empty()) { throw std::runtime_error("没有加载任何学生数据,无法保存"); return; } std::ofstream out(file); if(!out) { throw std::runtime_error("无法打开文件: " + file); } write(out); }


浙公网安备 33010602011771号