第八章 IO库

练习8.1

编写函数,接受一个istream&参数,返回值类型也是istream&。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。

解:

  1. std::istream& func(std::istream &is)
  2. {
  3. std::string buf;
  4. while (is >> buf)
  5. std::cout << buf << std::endl;
  6. is.clear();
  7. return is;
  8. }

练习8.2

测试函数,调用参数为cin

解:

  1. #include <iostream>
  2. using std::istream;
  3. istream& func(istream &is)
  4. {
  5. std::string buf;
  6. while (is >> buf)
  7. std::cout << buf << std::endl;
  8. is.clear();
  9. return is;
  10. }
  11. int main()
  12. {
  13. istream& is = func(std::cin);
  14. std::cout << is.rdstate() << std::endl;
  15. return 0;
  16. }

练习8.3

什么情况下,下面的while循环会终止?

  1. while (cin >> i) /* ... */

解:

badbitfailbiteofbit 的任一个被置位,那么检测流状态的条件会失败。

练习8.4

编写函数,以读模式打开一个文件,将其内容读入到一个stringvector中,将每一行作为一个独立的元素存于vector中。

解:

  1. void ReadFileToVec(const string& fileName, vector<string>& vec)
  2. {
  3. ifstream ifs(fileName);
  4. if (ifs)
  5. {
  6. string buf;
  7. while (getline(ifs, buf))
  8. vec.push_back(buf);
  9. }
  10. }

练习8.5

重写上面的程序,将每个单词作为一个独立的元素进行存储。
解:

  1. void ReadFileToVec(const string& fileName, vector<string>& vec)
  2. {
  3. ifstream ifs(fileName);
  4. if (ifs)
  5. {
  6. string buf;
  7. while (ifs >> buf)
  8. vec.push_back(buf);
  9. }
  10. }

练习8.6

重写7.1.1节的书店程序,从一个文件中读取交易记录。将文件名作为一个参数传递给main

解:

  1. #include <fstream>
  2. #include <iostream>
  3. #include "../ch07/ex7_26.h"
  4. using std::ifstream; using std::cout; using std::endl; using std::cerr;
  5. int main(int argc, char **argv)
  6. {
  7. ifstream input(argv[1]);
  8. Sales_data total;
  9. if (read(input, total))
  10. {
  11. Sales_data trans;
  12. while (read(input, trans))
  13. {
  14. if (total.isbn() == trans.isbn())
  15. total.combine(trans);
  16. else
  17. {
  18. print(cout, total) << endl;
  19. total = trans;
  20. }
  21. }
  22. print(cout, total) << endl;
  23. }
  24. else
  25. {
  26. cerr << "No data?!" << endl;
  27. }
  28. return 0;
  29. }

练习8.7

修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给main函数。

解:

  1. #include <fstream>
  2. #include <iostream>
  3. #include "../ch07/ex7_26.h"
  4. using std::ifstream; using std::ofstream; using std::endl; using std::cerr;
  5. int main(int argc, char **argv)
  6. {
  7. ifstream input(argv[1]);
  8. ofstream output(argv[2]);
  9. Sales_data total;
  10. if (read(input, total))
  11. {
  12. Sales_data trans;
  13. while (read(input, trans))
  14. {
  15. if (total.isbn() == trans.isbn())
  16. total.combine(trans);
  17. else
  18. {
  19. print(output, total) << endl;
  20. total = trans;
  21. }
  22. }
  23. print(output, total) << endl;
  24. }
  25. else
  26. {
  27. cerr << "No data?!" << endl;
  28. }
  29. return 0;
  30. }

练习8.8

修改上一题的程序,将结果追加到给定的文件末尾。对同一个输出文件,运行程序至少两次,检验数据是否得以保留。

解:

  1. #include <fstream>
  2. #include <iostream>
  3. #include "../ch07/ex7_26.h"
  4. using std::ifstream; using std::ofstream; using std::endl; using std::cerr;
  5. int main(int argc, char **argv)
  6. {
  7. ifstream input(argv[1]);
  8. ofstream output(argv[2], ofstream::app);
  9. Sales_data total;
  10. if (read(input, total))
  11. {
  12. Sales_data trans;
  13. while (read(input, trans))
  14. {
  15. if (total.isbn() == trans.isbn())
  16. total.combine(trans);
  17. else
  18. {
  19. print(output, total) << endl;
  20. total = trans;
  21. }
  22. }
  23. print(output, total) << endl;
  24. }
  25. else
  26. {
  27. cerr << "No data?!" << endl;
  28. }
  29. return 0;
  30. }

练习8.9

使用你为8.1.2节第一个练习所编写的函数打印一个istringstream对象的内容。

解:

  1. #include <iostream>
  2. #include <sstream>
  3. using std::istream;
  4. istream& func(istream &is)
  5. {
  6. std::string buf;
  7. while (is >> buf)
  8. std::cout << buf << std::endl;
  9. is.clear();
  10. return is;
  11. }
  12. int main()
  13. {
  14. std::istringstream iss("hello");
  15. func(iss);
  16. return 0;
  17. }

练习8.10

编写程序,将来自一个文件中的行保存在一个vector中。然后使用一个istringstreamvector读取数据元素,每次读取一个单词。

解:

  1. #include <iostream>
  2. #include <fstream>
  3. #include <sstream>
  4. #include <vector>
  5. #include <string>
  6. using std::vector; using std::string; using std::ifstream; using std::istringstream; using std::cout; using std::endl; using std::cerr;
  7. int main()
  8. {
  9. ifstream ifs("../data/book.txt");
  10. if (!ifs)
  11. {
  12. cerr << "No data?" << endl;
  13. return -1;
  14. }
  15. vector<string> vecLine;
  16. string line;
  17. while (getline(ifs, line))
  18. vecLine.push_back(line);
  19. for (auto &s : vecLine)
  20. {
  21. istringstream iss(s);
  22. string word;
  23. while (iss >> word)
  24. cout << word << endl;
  25. }
  26. return 0;
  27. }

练习8.11

本节的程序在外层while循环中定义了istringstream对象。如果record对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将record的定义移到while循环之外,验证你设想的修改方法是否正确。

解:

  1. #include <iostream>
  2. #include <sstream>
  3. #include <string>
  4. #include <vector>
  5. using std::vector; using std::string; using std::cin; using std::istringstream;
  6. struct PersonInfo {
  7. string name;
  8. vector<string> phones;
  9. };
  10. int main()
  11. {
  12. string line, word;
  13. vector<PersonInfo> people;
  14. istringstream record;
  15. while (getline(cin, line))
  16. {
  17. PersonInfo info;
  18. record.clear();
  19. record.str(line);
  20. record >> info.name;
  21. while (record >> word)
  22. info.phones.push_back(word);
  23. people.push_back(info);
  24. }
  25. for (auto &p : people)
  26. {
  27. std::cout << p.name << " ";
  28. for (auto &s : p.phones)
  29. std::cout << s << " ";
  30. std::cout << std::endl;
  31. }
  32. return 0;
  33. }

练习8.12

我们为什么没有在PersonInfo中使用类内初始化?

解:

因为这里只需要聚合类就够了,所以没有必要在PersionInfo中使用类内初始化。

练习8.13

重写本节的电话号码程序,从一个命名文件而非cin读取数据。

解:

  1. #include <iostream>
  2. #include <sstream>
  3. #include <fstream>
  4. #include <string>
  5. #include <vector>
  6. using std::vector; using std::string; using std::cin; using std::istringstream;
  7. using std::ostringstream; using std::ifstream; using std::cerr; using std::cout; using std::endl;
  8. using std::isdigit;
  9. struct PersonInfo {
  10. string name;
  11. vector<string> phones;
  12. };
  13. bool valid(const string& str)
  14. {
  15. return isdigit(str[0]);
  16. }
  17. string format(const string& str)
  18. {
  19. return str.substr(0,3) + "-" + str.substr(3,3) + "-" + str.substr(6);
  20. }
  21. int main()
  22. {
  23. ifstream ifs("../data/phonenumbers.txt");
  24. if (!ifs)
  25. {
  26. cerr << "no phone numbers?" << endl;
  27. return -1;
  28. }
  29. string line, word;
  30. vector<PersonInfo> people;
  31. istringstream record;
  32. while (getline(ifs, line))
  33. {
  34. PersonInfo info;
  35. record.clear();
  36. record.str(line);
  37. record >> info.name;
  38. while (record >> word)
  39. info.phones.push_back(word);
  40. people.push_back(info);
  41. }
  42. for (const auto &entry : people)
  43. {
  44. ostringstream formatted, badNums;
  45. for (const auto &nums : entry.phones)
  46. if (!valid(nums)) badNums << " " << nums;
  47. else formatted << " " << format(nums);
  48. if (badNums.str().empty())
  49. cout << entry.name << " " << formatted.str() << endl;
  50. else
  51. cerr << "input error: " << entry.name
  52. << " invalid number(s) " << badNums.str() << endl;
  53. }
  54. return 0;
  55. }

练习8.14

我们为什么将entrynums定义为const auto&

解:

它们都是类类型,因此使用引用避免拷贝。
在循环当中不会改变它们的值,因此用const