实验6

实验任务1:

contestant.hpp:
 1 #pragma once
 2 #include <iomanip>
 3 #include <iostream>
 4 #include <string>
 5 
 6 struct Contestant
 7 {
 8     long id;           // 学号
 9     std::string name;  // 姓名
10     std::string major; // 专业
11     int solved;        // 解题数
12     int penalty;       // 总罚时
13 };
14 
15 // 重载<<
16 // 要求:姓名/专业里不含空白符
17 inline std::ostream &operator<<(std::ostream &out, const Contestant &c)
18 {
19     out << std::left;
20     out << std::setw(15) << c.id
21         << std::setw(15) << c.name
22         << std::setw(15) << c.major
23         << std::setw(10) << c.solved
24         << std::setw(10) << c.penalty;
25 
26     return out;
27 }
28 
29 // 重载>>
30 inline std::istream &operator>>(std::istream &in, Contestant &c)
31 {
32     in >> c.id >> c.name >> c.major >> c.solved >> c.penalty;
33 
34     return in;
35 }
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 
 9 // ACM 排序规则:先按解题数降序,再按罚时升序
10 inline bool cmp_by_solve(const Contestant &a, const Contestant &b)
11 {
12     if (a.solved != b.solved)
13         return a.solved > b.solved;
14 
15     return a.penalty < b.penalty;
16 }
17 
18 // 将结果写至任意输出流
19 inline void write(std::ostream &os, const std::vector<Contestant> &v)
20 {
21     for (const auto &x : v)
22         os << x << '\n';
23 }
24 
25 // 将结果打印到屏幕
26 inline void print(const std::vector<Contestant> &v)
27 {
28     write(std::cout, v);
29 }
30 
31 // 将结果保存到文件
32 inline void save(const std::string &filename, const std::vector<Contestant> &v)
33 {
34     std::ofstream os(filename);
35     if (!os)
36         throw std::runtime_error("fail to open " + filename);
37 
38     write(os, v);
39 }
40 
41 // 从文件读取信息(跳过标题行)
42 inline std::vector<Contestant> load(const std::string &filename)
43 {
44     std::ifstream is(filename);
45     if (!is)
46         throw std::runtime_error("fail to open " + filename);
47 
48     std::string line;
49     std::getline(is, line); // 跳过标题
50 
51     std::vector<Contestant> v;
52     Contestant t;
53     int seq;
54     while (is >> seq >> t)
55         v.push_back(t);
56 
57     return v;
58 }
task1.cpp:
 1 #include <algorithm>
 2 #include <iostream>
 3 #include <stdexcept>
 4 #include <vector>
 5 #include "contestant.hpp"
 6 #include "utils.hpp"
 7 #include "windows.h"
 8 
 9 const std::string in_file = "./data.txt";
10 const std::string out_file = "./ans.txt";
11 
12 void app()
13 {
14     std::vector<Contestant> contestants;
15 
16     try
17     {
18         contestants = load(in_file);
19         std::sort(contestants.begin(), contestants.end(), cmp_by_solve);
20         print(contestants);
21         save(out_file, contestants);
22     }
23     catch (const std::exception &e)
24     {
25         std::cerr << e.what() << '\n';
26         return;
27     }
28 }
29 
30 int main()
31 {
32     SetConsoleOutputCP(CP_UTF8);
33     app();
34 }

运行结果:

屏幕截图 2025-12-23 143158

数据文件ans.txt:
屏幕截图 2025-12-23 143743

问题回答:

1.(1)std::cout 是 std::ostream 的直接实例,std::ofstream 继承自 std::ostream,

            std::ostream& 作为参数类型时,能接收任何 std::ostream 派生类的对象,因此write()能同时接受 std::cout 和 std::ofstream 对象作为实参。

   (2)不需要。只要该设备也提供 std::ostream 接口就能调用write()。

2.(1)

第一处:

1 inline void save(const std::string &filename, const std::vector<Contestant> &v)
2 {
3     std::ofstream os(filename);
4     if (!os)
5         throw std::runtime_error("fail to open " + filename);
6 
7     write(os, v);
8 }
抛出异常情况:打开指定文件(out_file)失败时。
第二处:
 1 inline std::vector<Contestant> load(const std::string &filename)
 2 {
 3     std::ifstream is(filename);
 4     if (!is)
 5         throw std::runtime_error("fail to open " + filename);
 6 
 7     std::string line;
 8     std::getline(is, line); // 跳过标题
 9 
10     std::vector<Contestant> v;
11     Contestant t;
12     int seq;
13     while (is >> seq >> t)
14         v.push_back(t);
15 
16     return v;
17 }

抛出异常情况:打开指定输入文件(in_file)失败时。

   (2)被try{}catch{}捕获,通过std::cerr << e.what() << '\n';进行输出,并终止程序。

3.可以,功能、性能、结果一致,都无额外开销。

4.(1)

屏幕截图 2025-12-23 194109

问题:读取不完整,输出产生乱码;

原因:is >> seq >> t 读取时,solved和penalty 无值,会导致读取失败,导致while循环终止,无法继续读取。

   (2)

 1 inline std::vector<Contestant> load(const std::string &filename)
 2 {
 3     std::ifstream is(filename);
 4     if (!is)
 5         throw std::runtime_error("fail to open " + filename);
 6 
 7     std::string line;
 8     std::getline(is, line); // 跳过标题
 9 
10     std::vector<Contestant> v;
11     Contestant t;
12     int seq;
13     int line_num = 1;
14     while(getline(is, line))
15     {
16         line_num++;
17         std::istringstream iss(line);
18         if(line.empty()||line.find_first_not_of(" \t\n\r") == std::string::npos) continue; 
19         if (!(iss >> seq >> t.id >> t.name >> t.major >> t.solved >> t.penalty))
20         {
21             std::cerr <<"Warning: invalid data format at line " << line_num << ", skipped.\n";
22             continue;
23         }
24         v.push_back(t);
25     }
26     return v;
27 }

 

实验任务2:

student.hpp:
 1 #pragma once
 2 
 3 #include <iostream>
 4 #include <string>
 5 
 6 class Student
 7 {
 8 public:
 9     Student() = default;
10     ~Student() = default;
11 
12     const std::string get_major() const;
13     int get_grade() const;
14 
15     friend std::ostream &operator<<(std::ostream &os, const Student &s);
16     friend std::istream &operator>>(std::istream &is, Student &s);
17 
18 private:
19     int id;
20     std::string name;
21     std::string major;
22     int grade; // 0-100
23 };
student.cpp:
 1 #include "student.hpp"
 2 #include <iomanip>
 3 #include <iostream>
 4 
 5 std::ostream &operator<<(std::ostream &os, const Student &s)
 6 {
 7     os << std::left;
 8     os << std::setw(15) << s.id
 9         << std::setw(15) << s.name
10         << std::setw(15) << s.major
11         << std::setw(15) << s.grade;
12     return os;
13 }
14 
15 std::istream &operator>>(std::istream &is, Student &s)
16 {
17     int temp_id;
18     std::string temp_name;
19     std::string temp_major;
20     int temp_grade;
21     if(!(is >> temp_id >> temp_name >> temp_major >> temp_grade))
22         throw std::runtime_error("Invalid input format for Student");
23     if(temp_grade < 0 || temp_grade > 100)
24         throw std::runtime_error("Grade must be between 0 and 100");
25     s.id = temp_id;
26     s.name = temp_name;
27     s.major = temp_major;
28     s.grade = temp_grade;
29     return is;
30 }
31 
32 const std::string Student::get_major() const
33 {
34     return major;
35 }
36 
37 int Student::get_grade() const
38 {
39     return grade;
40 }
stumgr.hpp:
 1 #pragma once
 2 #include <string>
 3 #include <vector>
 4 #include "student.hpp"
 5 
 6 class StuMgr
 7 {
 8 public:
 9     void load(const std::string &file);       // 加载数据文件(空格分隔)
10     void sort();                              // 排序: 按专业字典序升序、同专业分数降序
11     void print() const;                       // 打印到屏幕
12     void save(const std::string &file) const; // 保存到文件
13 
14 private:
15     void write(std::ostream &os) const; // 把数据写到任意输出流
16 
17 private:
18     std::vector<Student> students;
19 };
stumgr.cpp:
 1 #include "stumgr.hpp"
 2 #include <fstream>
 3 #include <algorithm>
 4 #include <iostream>
 5 #include <sstream>
 6 
 7 void StuMgr::load(const std::string &filename)
 8 {
 9     std::ifstream is(filename);
10     if (!is)
11         throw std::runtime_error("fail to open " + filename);
12 
13     std::string line;
14     getline(is, line);
15 
16     int line_num = 1;
17     while (getline(is, line))
18     {
19         std::istringstream iss(line);
20         line_num++;
21         if (line.empty() || line.find_first_not_of(" \t\n\r") == std::string::npos)
22         {
23             std::cerr << "[Warning] line " << line_num << " format error, skipped: ";
24             std::cout << line << '\n';
25             continue;
26         }
27         else
28         {
29             int temp_id;
30             std::string temp_name;
31             std::string temp_major;
32             int temp_grade;
33             if (!(iss >> temp_id >> temp_name >> temp_major >> temp_grade))
34             {
35                 std::cerr << "[Warning] line " << line_num << " format error, skipped: ";
36                 std::cout << line << '\n';
37                 continue;
38             }
39             else if (temp_grade < 0 || temp_grade > 100)
40             {
41                 std::cerr << "[Warning] line " << line_num << " grade invalid, skipped: ";
42                 std::cout << line << '\n';
43                 continue;
44             }
45             else
46             {
47                 {
48                     Student s;
49                     std::istringstream line_iss(line);
50                     try
51                     {
52                         line_iss >> s; 
53                     }
54                     catch (const std::runtime_error &e)
55                     {
56                         std::cerr << "[Warning] line " << line_num << " " << e.what() << ", skipped: ";
57                         std::cout << line << '\n';
58                         continue;
59                     }
60                     students.push_back(s);
61                 }
62             }
63         }
64     }
65 }
66 
67 void StuMgr::sort()
68 {
69     std::sort(students.begin(), students.end(), [](const Student &a, const Student &b)
70               {
71         if (a.get_major() != b.get_major())
72             return a.get_major() < b.get_major();
73         else
74             return a.get_grade() > b.get_grade(); });
75 }
76 
77 void StuMgr::print() const
78 {
79     write(std::cout);
80 }
81 
82 void StuMgr::save(const std::string &filename) const
83 {
84     std::ofstream os(filename);
85     if (!os)
86         throw std::runtime_error("fail to open " + filename);
87 
88     write(os);
89 }
90 
91 void StuMgr::write(std::ostream &os) const
92 {
93     for (const auto &s : students)
94     {
95         os << s << '\n';
96     }
97 }

task2.cpp:

 1 #include <iostream>
 2 #include <limits>
 3 #include <string>
 4 #include "stumgr.hpp"
 5 #include "Windows.h"
 6 
 7 const std::string in_file = "./data_bad.txt";
 8 const std::string out_file = "./ans.txt";
 9 
10 void menu()
11 {
12     std::cout << "\n**********简易应用**********\n"
13                  "1. 加载文件\n"
14                  "2. 排序\n"
15                  "3. 打印到屏幕\n"
16                  "4. 保存到文件\n"
17                  "5. 退出\n"
18                  "请选择:";
19 }
20 
21 void app()
22 {
23     StuMgr mgr;
24 
25     while (true)
26     {
27         menu();
28         int choice;
29         std::cin >> choice;
30 
31         try
32         {
33             switch (choice)
34             {
35             case 1:
36                 mgr.load(in_file);
37                 std::cout << "加载成功\n";
38                 break;
39             case 2:
40                 mgr.sort();
41                 std::cout << "排序已完成\n";
42                 break;
43             case 3:
44                 mgr.print();
45                 std::cout << "打印已完成\n";
46                 break;
47             case 4:
48                 mgr.save(out_file);
49                 std::cout << "导出成功\n";
50                 break;
51             case 5:
52                 return;
53             default:
54                 std::cout << "不合法输入\n";
55             }
56         }
57         catch (const std::exception &e)
58         {
59             std::cout << "Error: " << e.what() << '\n';
60         }
61     }
62 }
63 
64 int main()
65 {
66     SetConsoleOutputCP(CP_UTF8);
67     app();
68 }

运行结果:

屏幕截图 2025-12-23 231637

屏幕截图 2025-12-23 231755

 

posted @ 2025-12-23 23:20  noeleven  阅读(0)  评论(0)    收藏  举报