实验六
任务一:
contestant.hpp
1 #pragma once 2 #include<iomanip> 3 #include<iostream> 4 #include<iostream> 5 struct Contestant 6 { 7 long id; 8 std::string name; 9 std::string major; 10 int solved; 11 int penalty; 12 }; 13 inline std::ostream& operator<<(std::ostream& out,const Contestant &c) 14 { 15 out<<std::left; 16 out<<std::setw(15)<<c.id <<std::setw(15)<<c.name <<std::setw(15)<<c.major <<std::setw(15)<<c.solved <<std::setw(15)<<c.penalty ; 17 return out; 18 } 19 inline std::istream&operator>>(std::istream& in,Contestant& c) 20 { 21 in>>c.id>>c.name>>c.major >>c.solved >>c.penalty ; 22 return in; 23 }
utils.hpp
1 #pragma once 2 #include<fstream> 3 #include<iostream> 4 #include<stdexcept> 5 #include<string> 6 #include<vector> 7 #include"contestant.hpp" 8 inline bool com_by_solve(const Contestant& a,const Contestant& b) 9 { 10 if(a.solved!=b.solved ) 11 return a.solved>b.solved ; 12 return a.penalty <b.penalty ; 13 } 14 inline void write(std::ostream& os,const std::vector<Contestant>& v) 15 { 16 for(const auto&x:v) 17 os<<x<<'\n'; 18 } 19 inline void print(const std::vector<Contestant>& v) 20 { 21 write(std::cout,v); 22 } 23 inline void save(const std::string& filename,const std::vector<Contestant>& v) 24 { 25 std::ofstream os(filename); 26 if(!os) 27 throw std::runtime_error("fail to open"+filename); 28 write(os,v); 29 } 30 inline std::vector<Contestant> load(const std::string& filename) 31 { 32 std::ifstream is(filename); 33 if(!is) 34 throw std::runtime_error("fail to open"+filename); 35 std::string line; 36 std::getline(is,line); 37 std::vector<Contestant> v; 38 Contestant t; 39 int seq; 40 while(is>>seq>>t) 41 v.push_back(t); 42 return v; 43 44 }
task1.cpp
1 #include<algorithm> 2 #include<iostream> 3 #include<stdexcept> 4 #include<vector> 5 #include"contestant.hpp" 6 #include"utils.hpp" 7 const std::string in_file="./data.txt"; 8 const std::string out_file="./ans.txt"; 9 void app() 10 { 11 std::vector<Contestant> contestants; 12 try 13 { 14 contestants=load(in_file); 15 std::sort(contestants.begin(),contestants.end(),com_by_solve); 16 print(contestants); 17 save(out_file,contestants); 18 }catch(const std::exception& e) 19 { 20 std::cerr<<e.what()<<'\n'; 21 return ; 22 } 23 } 24 int main() 25 { 26 app(); 27 }
运行结果截图


问题1:流操作与代码复用
观察 print() 与 save() 的实现,均在内部调用write() :
(1)write() 的参数类型是 std::ostream& ,它为什么能同时接受 std::cout 和 std::ofstream 对象作为实参?
答:std::cout是std::ostream& 的对象,std::ofstream继承std::ostream
(2)如果要把结果写到其他设备,只要该设备也提供 std::ostream 接口,还需改动 write() 吗?
答:不需要改动write().
问题2:异常处理与捕获
在代码中找到两处 throw 语句,说明:
(1)什么情况下会抛出异常;
答:两句throw在save()和load()函数中,throw std::runtime_error("fail to open"+filename);在文件无法打开时会抛出异常
(2)异常被谁捕获、做了哪些处理。
答: 被app()函数中的try-catch块捕获
处理方式:使用std::cerr输出错误信息到标准错误流,用return提前结束函数,退出
问题3:替代写法)
函数app中std::sort(contestants.begin(), contestants.end(), cmp_by_solve); 参数
cmp_by_solve 换成下面的lambda表达式是否可以?功能、性能、结果是否一致
可以,功能、性能、结果一致。
问题4:数据完整性与代码健壮性
把 in_file 改成 "./data_bad.txt" (内含空白行或字段缺失),重新编译运行:
(1)观察运行结果有什么问题?给出测试截图并分析原因。
测试截图

答:数据读取错误,部分数据缺失。原因:data_bad中没有Thomas的解题数和总罚时,导致读取了下一行的学号,while终止,后面数据没被读到
(2)思考:如何修改函数 std::vector load(const std::string& filename) ,使其提示出 错行号并跳过有问题的数据行,同时让程序继续执行?(选答*)
1 #pragma once 2 #include<fstream> 3 #include<iostream> 4 #include<stdexcept> 5 #include<string> 6 #include<vector> 7 #include"contestant.hpp" 8 inline bool com_by_solve(const Contestant& a,const Contestant& b) 9 { 10 if(a.solved!=b.solved ) 11 return a.solved>b.solved ; 12 return a.penalty <b.penalty ; 13 } 14 inline void write(std::ostream& os,const std::vector<Contestant>& v) 15 { 16 for(const auto&x:v) 17 os<<x<<'\n'; 18 } 19 inline void print(const std::vector<Contestant>& v) 20 { 21 write(std::cout,v); 22 } 23 inline void save(const std::string& filename,const std::vector<Contestant>& v) 24 { 25 std::ofstream os(filename); 26 if(!os) 27 throw std::runtime_error("fail to open"+filename); 28 write(os,v); 29 } 30 inline std::vector<Contestant> load(const std::string& filename) 31 { 32 std::ifstream is(filename); 33 if(!is) 34 throw std::runtime_error("fail to open"+filename); 35 std::string line; 36 std::getline(is,line); 37 std::vector<Contestant> v; 38 Contestant t; 39 int seq; 40 int count=0; 41 while(std::getline(is,line)) 42 { 43 count++ ; 44 if(line.empty() ) 45 { 46 std::cerr << "Warning: 第" <<count << "行为空白行,已跳过" << std::endl; 47 continue; 48 } 49 std::istringstream iss(line); 50 if(iss>>seq>>t) 51 v.push_back(t); 52 else 53 { 54 std::cerr<< "Warning: 第" << count << "行错误,已跳过" << std::endl; 55 continue; 56 } 57 } 58 return v; 59 60 }
运行结果

任务二
student.hpp
1 #pragma once 2 #include<string> 3 class Student 4 { 5 public: 6 Student()=default; 7 ~Student()=default; 8 const std::string get_major() const; 9 int get_grade() const; 10 friend std::ostream& operator<<(std::ostream& os,const Student& s); 11 friend std::istream& operator>>(std::istream& is, Student& s); 12 private: 13 int id; 14 std::string name; 15 std::string major; 16 int grade; 17 };
student.cpp
1 #include <iostream> 2 #include <iomanip> 3 #include"student.hpp" 4 int Student::get_grade() const 5 { 6 return grade; 7 } 8 const std::string Student::get_major() const 9 { 10 return major; 11 } 12 std::ostream& operator<<(std::ostream& os,const Student& s) 13 { 14 os << std::left 15 << std::setw(15) << s.id 16 << std::setw(15) << s.name 17 << std::setw(10) << s.major 18 << std::setw(10) <<s.grade; 19 return os; 20 } 21 std::istream& operator>>(std::istream& is, Student& s) 22 { 23 is>>s.id>>s.name>>s.major>>s.grade; 24 return is; 25 }
stumgr.hpp
1 #pragma once 2 #include <string> 3 #include <vector> 4 #include "student.hpp" 5 class StuMgr 6 { 7 public: 8 void load(const std::string& file); 9 void sort(); 10 void print() const; 11 void save(const std::string& file) const; 12 private: 13 void write(std::ostream &os) const; 14 private: 15 std::vector<Student> students; 16 };
stumgr.cpp
1 #include<iostream> 2 #include<fstream> 3 #include<algorithm> 4 #include<stdexcept> 5 #include<iomanip> 6 #include"stumgr.hpp" 7 const std::string in_file = "./data.txt"; 8 const std::string out_file = "./ans.txt"; 9 void StuMgr::load(const std::string& file) 10 { 11 std::ifstream is(file); 12 if(!is) 13 throw std::runtime_error("fail to open"+file); 14 Student t; 15 std::string line; 16 std::getline(is, line); 17 while (is >> t) 18 { 19 students.push_back(t); 20 } 21 } 22 void StuMgr::sort() 23 { 24 25 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();}); 26 } 27 void StuMgr::print() const 28 { 29 30 for (const auto& student : students) 31 std::cout << student << std::endl; 32 33 } 34 void StuMgr::save(const std::string& file) const 35 { 36 37 std::ofstream os(file); 38 if(!os) 39 throw std::runtime_error("fail to open "+file); 40 write(os); 41 } 42 void StuMgr::write(std::ostream &os) const 43 { 44 for(const auto& student:students) 45 os<<student<<std::endl; 46 }
task2.cpp
1 #include <iostream> 2 #include <limits> 3 #include <string> 4 #include "stumgr.hpp" 5 const std::string in_file = "./data.txt"; 6 const std::string out_file = "./ans.txt"; 7 void menu() 8 { 9 std::cout << "\n**********简易应用**********\n" 10 "1. 加载文件\n" 11 "2. 排序\n" 12 "3. 打印到屏幕\n" 13 "4. 保存到文件\n" 14 "5. 退出\n" 15 "请选择:"; 16 } 17 void app() 18 { 19 StuMgr mgr; 20 while(true) 21 { 22 menu(); 23 int choice; 24 std::cin >> choice; 25 try 26 { 27 switch(choice) 28 { 29 case 1:mgr.load(in_file); 30 std::cout<<"加载成功\n"; 31 break; 32 case 2: mgr.sort(); 33 std::cout << "排序已完成\n"; break; 34 case 3: mgr.print(); 35 std::cout << "打印已完成\n"; break; 36 case 4: mgr.save(out_file); 37 std::cout << "导出成功\n"; break; 38 case 5: return; 39 default: std::cout << "不合法输入\n"; 40 } 41 } 42 catch (const std::exception& e) 43 { 44 std::cout << "Error: " << e.what() << '\n'; 45 } 46 } 47 } 48 int main() 49 { 50 app(); 51 }
运行结果截图




选做:
说明:在加载时判断成绩是否在0-100内,有无超出预期。提示错误信息时展示出是哪行。也可以识别空白行和字段为空。
1 void StuMgr::load(const std::string& file) 2 { 3 std::ifstream is(file); 4 if(!is) 5 throw std::runtime_error("fail to open"+file); 6 Student t; 7 std::string line; 8 std::getline(is,line); 9 int count=1; 10 while(std::getline(is,line)) 11 { 12 count++ ; 13 if(line.empty() ) 14 { 15 std::cerr << "[Warning] line " <<count << " empty error, skipped" << std::endl; 16 continue; 17 } 18 std::istringstream iss(line); 19 if(iss>>t) 20 { 21 if(t.get_grade()>100||t.get_grade() <0) 22 { 23 std::cerr<<"error: "<<t.get_grade()<<std::endl ; 24 std::cerr<< "[Warning] line " << count <<" grade invalid, skipped: " <<line<< std::endl; 25 continue; 26 } 27 students.push_back(t); 28 } 29 else 30 { 31 std::cerr<< "[Warning] line " << count <<" format error, skipped: " <<line<< std::endl; 32 continue; 33 34 } 35 } 36 }

总结
我更加深入了解文件I/O与异常处理,通过throw,try,catch的组合,cerr的调用实现文件打开失败时抛出异常,数据格式错误时给出详细警告,空行和无效数据被跳过。

浙公网安备 33010602011771号