《C++ Primer》第8章 IO库
8.3 string流 习题答案
练习8.9:使用你为8.1.2节(第281页)第一个练习编写的函数打印一个sitringstream对象的内存。
【出题思路】
本题练习字符串流的输入。
【解答】
#include#include #include #include #include using namespace std; istream& f(istream &in) { string v; while(in >> v, !in.eof()) //直到遇到文件结束符才停止读取 { if(in.bad()) { throw runtime_error("IO流错误"); } if(in.fail()) { cerr << "数据错误,请重试:" << endl; in.clear(); in.ignore(100, 'n'); continue; } cout << v << endl; } in.clear(); return in; } int main(int argc, char *argv[]) { ostringstream msg; //字符串输出流 msg << "C++ Primer 第五版" << endl; istringstream in(msg.str()); f(in); return 0; }
运行结果:
练习8.10:编写程序,将来自一个文件中的行保存在一个vector
【出题思路】
本题继续练习字符串流的输入。
【解答】
#include#include #include #include #include using namespace std; int main() { //打开文件 ifstream in("../chapter08/data08_10.txt"); if(!in) { cerr << "无法打开输入文件" << endl; return -1; } string line; vector words; while(getline(in, line)) //从文件中读取一行 { words.push_back(line); //添加到vector中 } in.close(); //输入完毕,关闭文件 vector ::const_iterator it = words.begin(); //迭代器 while(it != words.end()) //遍历vector { istringstream line_str(*it); string word; while(line_str >> word) //通过istringstream从vector中读取数据 { cout << word << " "; } cout << endl; ++it; } cout << "run sucess============" << endl; return 0; }
运行结果:
练习8.11:本节的程序在外层while循环中定义了sitringstream对象。如果record对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将record的定义移到while循环之外,验证你设想的修改方法是否正确。
【出题思路】
本题练习字符串流的重复使用,每次通过str成员将流绑定到不同的字符串,同时还要调用clear来重置流的状态。
【解答】
#include#include #include #include using namespace std; struct PersonInfo { string name; vector phones; }; int main(int argc, char *argv[]) { string line, word; //分别保存来自输入的一行和单词 vector people; //保存来自输入的所有记录 istringstream record; cout << "please input data" << endl; while(getline(cin, line)) { PersonInfo info; //创建一个保存此记录数据的对象 record.clear(); //重复使用字符串流时,每次都要调用clear record.str(line); //将记录绑定到则读入的行 record >> info.name; //读取名字 while(record >> word) //读取电话号码 { info.phones.push_back(word); //保持它们 } people.push_back(info); //将此记录追加到people末尾 } cout << "people.size=============" << people.size() << endl; return 0; }
运行结果:
练习8.12:我们为什么没有在PersonInfo中使用类内初始化?
【出题思路】
体会根据应用特点决定程序设计策略。
【解答】
由于每个人的电话号码数量不固定,因此更好的方式不是通过类内初始化指定人名和所有电话号码,而是在缺省初始化之后,在程序中设置人名并逐个添加电话号码。
练习8.13:重写本节的电话号码程序,从一个命名文件而非cin读取数据。
【出题思路】
本题练习文件流和字符串流输入输出的综合应用。
【解答】
#include#include #include #include #include using namespace std; struct PersonInfo { string name; vector phones; }; string format(const string &s) { return s; } bool valid(const string &s) { //如何验证电话号码将在第17章介绍 //现在简单返回true return true; } int main(int argc, char *argv[]) { string line, word;//分别保存来自输入的一行和单词 vector people;//保存来自输入的所有记录 istringstream record; if(argc != 2) { cerr << "请给出文件名" << endl; return -1; } ifstream in(argv[1]); if(!in) { cerr << "请给打开输入文件" << endl; return -1; } while(getline(in, line)) { PersonInfo info; //创建一个保存此记录数据的对象 record.clear(); //重复使用字符串流时,每次都要调用clear record.str(line); //将记录绑定到刚读入的行 record >> info.name; //读取名字 while(record >> word) //读取电话号码 { info.phones.push_back(word); //保持它们 } people.push_back(info); //将此记录追加到people末尾 } ostringstream os; for(const auto &entry: people) //对people中每一项 { ostringstream formatted, badNums; //每个循环步创建的对象 for(const auto &nums: entry.phones) //对每个数 { if(!valid(nums)) { badNums << " " << nums; //将数的字符串形式存入badNums } else { //将格式化的字符串“写入”formatted formatted << " " << format(nums); } if(badNums.str().empty()) //没有错误的数,打印名字和格式化的数 { os << entry.name << " " << formatted.str() << endl; } else //否则,打印名字和错误的数据 { cerr << "input error:" << entry.name << " invalid number(s) " << badNums.str() << endl; } } } cout << "os.str()=============n" << os.str() << endl; return 0; }
运行结果:
练习8.14:我们为什么将entry和nums定义为const auto&?
【出题思路】
回顾范围for语句的相关内容。
【解答】
这两条语句分别使用范围for语句枚举people中所有项(人)和每项的phones中的所有项(电话号码)。使用const表明在循环中不会改变这些项的值;auto是请求编译器依据vector元素类型来推断出entry和nums的类型,既简化代码又避免出错;使用引用的原因是,people和phones的元素分别是结构对象和字符串对象,使用引用即可避免对象拷贝。



