实验6
实验任务1
源代码:
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(15) << c.solved
<< std::setw(15) << 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 <sstream>
#include <cctype>
#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 = 0;
std::size_t line_no = 1;
while (std::getline(is, line)) {
++line_no;
bool all_ws = true;
for (char ch : line) {
if (!std::isspace(static_cast<unsigned char>(ch))) {
all_ws = false;
break;
}
}
if (all_ws) {
continue;
}
std::istringstream iss(line);
if (!(iss >> seq >> t.id >> t.name >> t.major >> t.solved >> t.penalty)) {
std::cerr << "warning: malformed input at line " << line_no << ": " << line << '\n';
continue;
}
v.push_back(t);
}
return v;
}
task1.cpp
点击查看代码
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>
#include "contestant.hpp"
#include "utlis.hpp"
const std::string in_file = R"(D:\实验6部分代码及数据文件_gbk\实验6部分代码及数据文件_gbk\1\data_bad.txt)";
const std::string out_file = R"(D:\实验6部分代码及数据文件_gbk\实验6部分代码及数据文件_gbk\1\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() {
app();
}
运行截图

缺少数据或者某一行没有数据就会跳过该行

问题
问题1:
(1)std::cout是std::ostream的一个对象的引用,std::ofstream是从是从std::basic_ofstream派生。函数参数是对基类std::ostream的引用,C++ 的引用/继承允许把派生类对象绑定到基类引用,从而复用同一函数来输出到不同类型的流
(2)不需要修改 write()。只要目标设备能以 std::ostream(或其派生类)暴露写接口,就可以直接传入。
问题2:
两处throw分别在save()和load()函数里面
(1)在save()里面打开输出文件失败以后,会抛出异常,在load()里面打开输入文件失败以后会抛出异常。
(2)异常被app()中的
try {
contestants = load(in_file);
...
save(out_file, contestants);
} catch (const std::exception& e) {
std::cerr<< e.what() << '\n';
return;
}
所有抛出的异常会被捕捉时,what返回fail to open
问题3:
cmp_by_solve 换成下面的lambda表达式之后,功能是一样的,如果a.solved 不等于 b.solved,返回a.solved>b.solved,否则返回a.solved>b.solved
性能主要是sort()决定,比较函数微小几乎不影响性能。结果是一样的。
问题4:

我增加以下代码
点击查看代码
while (std::getline(is, line)) {
++line_no;
bool all_ws = true;
for (char ch : line) {
if (!std::isspace(static_cast<unsigned char>(ch))) {
all_ws = false;
break;
}
}
if (all_ws) {
continue;
}
std::istringstream iss(line);
if (!(iss >> seq >> t.id >> t.name >> t.major >> t.solved >> t.penalty)) {
std::cerr << "warning: malformed input at line " << line_no << ": " << line << '\n';
continue;
}
v.push_back(t);
}
通过std::getline(is,line)按行读取,对每行用 std::istringstream 解析,这样一行解析失败不会破坏整个流。
std::isspace 检查每个字符是否为空白,如果整行都是空白,all_ws最后就是1,那么就跳过该行。
最后用流提取操作iss >> seq >> t.id >> t.name >> t.major >> t.solved >> t.penalty按空白分隔依次读取字段,如果不匹配抛出错误,跳过该行。
实验任务2
源代码:
student.hpp
点击查看代码
#pragma once
#include <iostream>
#include <string>
class Student {
public:
Student() = default;
Student(int id, const std::string& name, const std::string& major, int grade);
~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;
};
stumgr.cpp
点击查看代码
#include "stumgr.hpp"
#include <fstream>
#include <stdexcept>
#include <algorithm>
#include <iostream>
#include <sstream>
inline bool compare_(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::load(const std::string& file) {
std::ifstream is(file);
if (!is) {
throw std::runtime_error("fail to open " + file);
}
students.clear();
std::string line;
std::getline(is, line);
size_t line_no = 1;
while (std::getline(is, line)) {
++line_no;
if (line.empty()) continue;
std::istringstream ls(line);
Student s;
if (!(ls >> s)) {
throw std::runtime_error("parse error in " + file + " at line " + std::to_string(line_no) + ": '" + line + "'");
}
// 检查是否存在多余字段
std::string extra;
if (ls >> extra) {
throw std::runtime_error("extra fields in " + file + " at line " + std::to_string(line_no) + ": '" + extra + "'");
}
//检查分数是否合理
if (s.get_grade() < 0 || s.get_grade() > 100) {
throw std::runtime_error("invalid grade (not 0-100) in " + file + " at line " + std::to_string(line_no) + ": " + std::to_string(s.get_grade()));
}
students.push_back(s);
}
}
void StuMgr::sort() {
std::sort(students.begin(), students.end(), compare_);
}
void StuMgr::write(std::ostream& os)const {
for (const auto& s : students)
os << s << '\n';
}
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);
}
student.cpp
点击查看代码
#include "student.hpp"
#include <iomanip>
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(15) << 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 "stumgr.hpp"
const std::string in_file = "D:\\C语言代码\\实验六.2\\data_bad.txt";
const std::string out_file = "D:\\C语言代码\\实验六.2\\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();
}
运行截图
打印

排序后打印

拓展
我在studmgr.cpp文件的load()函数里面
(a)添加了数据缺失检测,

如果数据不是规定的格式,就会返回那一行的行数并且显示文件名,将问题抛出,这样方便找到报错原因。
(b)我还增加了检验分数是否合理的代码,

规定分数在0-100之间,否则抛出问题。
测试截图:
在data_bad.txt文件中数据缺失检测

不合法成绩检测


总结
(1)通过实验,我了解了流的抽象与复用,用 std::ostream& 编写 write(),同一函数可接受 std::cout、std::ofstream、std::ostringstream 等输出目标。
(2)学会了sort()函数的使用,第三个参数是用来定义排序的规则,可以实现升序降序或者其他形式的排序,功能强大。
(3)对于异常处理与错误报告。使用std::runtime_error可以在运行时触发,抛出错误信息,可用于检测是否打开文件。

浙公网安备 33010602011771号