实验6
一、实验任务1
源代码contestant.hpp
1 #pragma once 2 #include<iomanip> 3 #include<iostream> 4 #include<string> 5 struct Contestant{ 6 long id; 7 std::string name; 8 std::string major; 9 int solved; 10 int penalty; 11 }; 12 inline std::ostream& operator<<(std::ostream& out,const Contestant& c){ 13 out<<std::left; 14 out<<std::setw(15)<<c.id 15 <<std::setw(15)<<c.name 16 <<std::setw(15)<<c.major 17 <<std::setw(10)<<c.solved 18 <<std::setw(10)<<c.penalty; 19 return out; 20 } 21 inline std::istream& operator>>(std::istream& in,Contestant& c){ 22 in>>c.id>>c.name>>c.major>>c.solved>>c.penalty; 23 return in; 24 }
源代码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 cmp_by_solve(const Contestant& a,const Contestant& b){ 9 if(a.solved!=b.solved) 10 return a.solved>b.solved; 11 return a.penalty<b.penalty; 12 } 13 inline void write(std::ostream& os,const std::vector<Contestant>& v){ 14 for(const auto& x:v) 15 os<<x<<'\n'; 16 } 17 inline void print(const std::vector<Contestant>& v){ 18 write(std::cout,v); 19 } 20 inline void save(const std::string& filename,const std::vector<Contestant>& v){ 21 std::ofstream os(filename); 22 if(!os) 23 throw std::runtime_error("fail to open"+filename); 24 write(os,v); 25 } 26 inline std::vector<Contestant> load(const std::string& filename){ 27 std::ifstream is(filename); 28 if(!is) 29 throw std::runtime_error("fail to open"+filename); 30 std::string line; 31 std::getline(is,line); 32 std::vector<Contestant> v; 33 Contestant t; 34 int seq; 35 while(is>>seq>>t) 36 v.push_back(t); 37 return v; 38 }
源代码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 std::vector<Contestant> contestants; 11 try{ 12 contestants=load(in_file); 13 std::sort(contestants.begin(),contestants.end(),cmp_by_solve); 14 print(contestants); 15 save(out_file,contestants); 16 }catch(const std::exception& e){ 17 std::cerr<<e.what()<<'\n'; 18 return; 19 } 20 } 21 int main(){ 22 app(); 23 }
运行成果展示


问题1: 流操作与代码复用
答:std::ostream是c++标准输出流的一个基类,这里write()的参数类型是 std::ostream&表示是对基类std::ostream的引用。根据我在c++参考手册上查到的,std::cout是标准库提供的全局ostream对象(标准输出流),std::ofstream是继承自ostream的一个派生对象(文件输出流),所以都可以被ostream的引用绑定。



答:不需要改动,有了std::ostream接口可以将结果写进任意输出流。
![]()
![]()


答:可以。功能、性能、结果都一致,只不过不需要在外部定义一个比较函数了。

答:主要是利用了string中的empty()判断空行和利用istringstream(添加头文件"sstream")从字符串中提取数据来判断有没有数据缺失。每读入一行,先判断是否空行,是的话直接退出访问下一行;反之用istringstream获取此行中的所有元素,若完美匹配到每一个数据,放入容器中,否则就直接输出错误信息。
修改后的源代码utils.hpp(主要改了load函数,其他的源代码都没变)
1 #pragma once 2 #include<fstream> 3 #include<iostream> 4 #include<stdexcept> 5 #include<string> 6 #include<vector> 7 #include<sstream> 8 #include"contestant.hpp" 9 inline bool cmp_by_solve(const Contestant& a,const Contestant& b){ 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 for(const auto& x:v) 16 os<<x<<'\n'; 17 } 18 inline void print(const std::vector<Contestant>& v){ 19 write(std::cout,v); 20 } 21 inline void save(const std::string& filename,const std::vector<Contestant>& v){ 22 std::ofstream os(filename); 23 if(!os) 24 throw std::runtime_error("fail to open"+filename); 25 write(os,v); 26 } 27 inline std::vector<Contestant> load(const std::string& filename){ 28 std::ifstream is(filename); 29 if(!is) 30 throw std::runtime_error("fail to open"+filename); 31 std::string line; 32 std::getline(is,line); 33 std::vector<Contestant> v; 34 Contestant t; 35 int seq=1; 36 int num=1; 37 while(std::getline(is,line)) 38 { 39 num++; 40 if(line.empty()){ 41 std::cerr<<"警告:出现空行!出错行号为:"<<num<<",成功跳过此数据行\n"; 42 continue; 43 } 44 std::istringstream a(line); 45 if(a>>seq>>t.id>>t.name>>t.major>>t.solved>>t.penalty) 46 v.push_back(t); 47 else 48 std::cerr<<"警告:数据有缺!出错行号为:"<<num<<",成功跳过此数据行\n"; 49 seq++; 50 } 51 return v; 52 }


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




拓展:
在stumgr.cpp的load函数中增加判断条件,首先利用getline读取文件中每一行的信息,如果这一行是空行,输出空行错误;利用istringstream读取一行中的数据信息,如果读取的能对应每一个元素,再判断成绩的范围,输出越界错误,反之将这一行存放到容器中;如果一行中有数据空缺,输出格式错误。
源代码stumger.cpp(其他的源代码都没有修改,只调整了load函数的功能,在加载数据时可以报错)
1 #include<fstream> 2 #include<iostream> 3 #include<stdexcept> 4 #include<string> 5 #include<vector> 6 #include<algorithm> 7 #include<sstream> 8 #include"stumgr.hpp" 9 #include"student.hpp" 10 const std::string in_file="./data2.txt"; 11 const std::string out_file="./ans2.txt"; 12 void StuMgr::load(const std::string& file){ 13 std::ifstream is(file); 14 if(!is) 15 throw std::runtime_error("fail to open"+file); 16 std::string line; 17 std::getline(is,line); 18 Student s; 19 int seq=1; 20 while(std::getline(is,line)) 21 { 22 seq++; 23 if(line.empty()){ 24 std::cerr<<"[Warning] line "<<seq<<" empty line, skipped\n"; 25 } 26 else{ 27 std::istringstream a(line); 28 if(a>>s) 29 { 30 if(s.get_grade()<0||s.get_grade()>100){ 31 std::cout<<"error: "<<s.get_grade()<<std::endl; 32 std::cerr<<"[Warning] line "<<seq<<" grade invalid, skipped: "<<line<<std::endl; 33 } 34 else 35 students.push_back(s); 36 } 37 else 38 std::cerr<<"[Warning] line "<<seq<<" format error, skipped: "<<line<<std::endl; 39 } 40 } 41 } 42 void StuMgr::sort(){ 43 std::sort(students.begin(),students.end(), 44 [](const Student& a,const Student& b){ 45 return a.get_major()!=b.get_major()?a.get_major()<b.get_major() 46 :a.get_grade()>b.get_grade(); 47 }); 48 } 49 void StuMgr::print() const{ 50 write(std::cout); 51 } 52 void StuMgr::save(const std::string& file) const{ 53 std::ofstream os(file); 54 if(!os) 55 throw std::runtime_error("fail to open"+file); 56 write(os); 57 } 58 void StuMgr::write(std::ostream &os) const{ 59 for(const auto& x:students) 60 os<<x<<'\n'; 61 }




实验总结:
1.本次实验实践了利用标准IO流完成文件的读写来进行后续操作。在写文件的地址时有两种方法,一种直接按实例中给出的"./data.txt"(这是文件和代码要放在同一个文件夹里),另一种是绝对路径R"(C:\Users\Lenovo\Desktop\c++面向对象\data.txt)",就是要注意加上R和括号。
2.在重载运算符时要注意写清楚对应的元素,一开始我在任务2std::istream& operator>>中多加了s.seq,但没有由于没有这个元素导致文件整个无法打印出来。
3.在判断数据格式异常时用到了std::istringstream来获取一行中的所有数据,来寻找每一个数据是否都存在,但注意要加上头文件#include<sstream>。


浙公网安备 33010602011771号