实验6

Task1

源代码

Contestant.h
#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.h
#pragma once
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "contestant.h"

// 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;
}
demo1.cpp
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>
#include "contestant.h"
#include "utils.h"
#include <Windows.h>

const std::string in_file = "./data.txt";
const std::string out_file = "./ans.txt";

void app() {
    std::vector<Contestant> contestants;

    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;
    }
}

int main() {
    SetConsoleOutputCP(65001);
    app();
}

运行截图

Task1

问题

问题1:流操作与代码复用
观察 print() 与 save() 的实现,均在内部调用 write() :
(1) write() 的参数类型是 std::ostream& ,它为什么能同时接受 std::cout 和 std::ofstream 对象作
为实参?
(2)如果要把结果写到其他设备,只要该设备也提供 std::ostream 接口,还需改动 write() 吗?

(1)std::cout 和 std::ofstream都是std::ostream的派生类
(2)不需要

问题2:异常处理与捕获
在代码中找到两处 throw 语句,说明:
(1)什么情况下会抛出异常;
(2)异常被谁捕获、做了哪些处理。

(1)第一处:
if (!os)
        throw std::runtime_error("fail to open " + filename);
保存的文件不能正常打开
第二处:
if (!is)
        throw std::runtime_error("fail to open " + filename);
读取的文件不能正常打开
(2)都在task1.cpp文件中的app()部分被捕获,处理:打印错误信息并结束app(),停止后续程序运行

问题3:替代写法
函数 app 中 std::sort(contestants.begin(), contestants.end(), cmp_by_solve); 参数
cmp_by_solve 换成下面的lambda表达式是否可以?功能、性能、结果是否一致?

可以换成lambda表达式。功能、性能、结果一致

问题4:数据完整性与代码健壮性
把 in_file 改成 "./data_bad.txt" (内含空白行或字段缺失),重新编译运行:
(1)观察运行结果有什么问题?给出测试截图并分析原因。
(2)思考:如何修改函数 std::vector load(const std::string& filename) ,使其提示出
错行号并跳过有问题的数据行,同时让程序继续执行?(选答*)

(1)
存在缺失数据的那一行存在问题。原因:数据读入有问题,导致后续的数据读入也存在问题。

Task1(2)

Task2

源代码

StuMgr.cpp
#include "StuMgr.h"
#include "Student.h"

#include <fstream>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <iostream>
#include <cctype>

void StuMgr::load(const std::string& file) {
    std::ifstream is(file);
    if (!is) {
        throw std::runtime_error("fail to open" + file);
    }
    std::string str;
    std::getline(is, str);
    Student x;
    while (is >> x) students.push_back(x);
}

void StuMgr::sort() {
    std::sort(students.begin(), students.end(), [](const Student& s1, const Student& s2) {
        return s1.get_major() != s2.get_major() ? s1.get_major() < s2.get_major() : s1.get_grade() > s2.get_grade();
        });
}
//打印到屏幕
void StuMgr::print() const { 
    write(std::cout);
}
//保存到文件
void StuMgr::save(const std::string& file) const { 
    std::ofstream os(file);
    if (!os) {
        throw std::runtime_error("fail to open" + file);
    }
    write(os);
}
//把数据写到任意输出流
void StuMgr::write(std::ostream& os) const { 
    for (auto s : students) {
        os << s << "\n";
    }
}
StuMgr.h
#pragma once
#include <string>
#include <vector>
#include "Student.h"
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;
};
Student.h
#pragma once
#include <iostream>
#include <iomanip>
#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
};
Student.cpp
#include "Student.h"

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 << std::left;
	os << std::setw(15) << s.id
		<< std::setw(15) << s.name
		<< std::setw(15) << s.major
		<< std::setw(10) << s.grade;
	return os;
}
std::istream& operator>>(std::istream& is, Student& s) {
	is >> s.id >> s.name >> s.major >> s.grade;

	return is;
}
task2.cpp
#include <iostream>
#include <limits>
#include <string>
#include <Windows.h>

#include "StuMgr.h"
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 396

运行截图

Task2

posted @ 2025-12-23 15:26  永和九年2  阅读(5)  评论(0)    收藏  举报