实验六

任务一:

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  }

运行结果截图

image

image

问题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)观察运行结果有什么问题?给出测试截图并分析原因。

测试截图

2

答:数据读取错误,部分数据缺失。原因: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 }

运行结果

image

 任务二

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 }

运行结果截图

屏幕截图 2025-12-19 162222

屏幕截图 2025-12-19 162235

屏幕截图 2025-12-19 162518

image

 

选做:

说明:在加载时判断成绩是否在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 }

 

image

 总结

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

posted @ 2025-12-19 21:13  零和星际  阅读(0)  评论(0)    收藏  举报