c++ Primer 第八章:IO库 练习答案记录
练习题导航
下面的练习别忘记都加上下面这一语句
#include<iostream>
8.1 IO类
8.1.1 IO对象无拷贝或赋值
8.1.1 IO对象无拷贝或赋值
8.1.2 条件状态
练习8.1 编写函数,接受一个istream&参数,返回值类型也是istream&。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。
using namespace std;
istream& is(istream& is)
{
string s;
while (is >> s)
cout << s << endl;
is.clear();
return is;
}
练习8.2 测试函数,调用参数为cin
using namespace std;
istream& is(istream& is)
{
string s;
while (is >> s)
cout << s << endl;
is.clear();
return is;
}
int main()
{
is(cin);
string s1;
while (cin >> s1)
cout << s1 << endl;
}
练习8.3 什么情况下,下面的while循环会终止?
badbit、failbit和eofbit任一个被置位,则检测流状态的条件会失败。
while(cin>>i)
8.1.3 管理输出缓冲
8.2 文件输入输出
8.2.1 使用文件流对象
练习8.4 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个独立的元素存于vector中
首先在一个目录下创建一个txt文件,我这里在E:\C++练习\test8目录下创建了一个input的txt文件
文件内容如下
点击另存为,编码选择ANSI(不然中文打出来会乱码)
主程序,记住文件名处是两个斜杠才能识别
#include<fstream> //文件输入输出
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> inp;
string input;
ifstream in("E:\\C++练习\\test8\\input.txt"); //路径这里要加两个\\单独一个\无法读取文件
if (in) {
while (getline(in, input))
{
inp.push_back(input);
}
}
for (const auto& s : inp)
cout << s << endl;
}
输出:
练习8.5 重写上面的程序,将每个单词作为一个独立的元素进行存储
#include<fstream>
#include<string>
#include<vector>
using namespace std;
int main()
{
string input;
vector<string> inp;
ifstream in("E:\\C++练习\\test8\\input.txt");
if (in) {
while (in >> input) {
inp.push_back(input);
}
}
for (const auto& s : inp)
cout << s << endl;
}
练习8.6 重写7.1.1节的书店程序(第229页),从一个文件中读取交易记录。将文件名作为一个参数传递给main(参见6.2.5节,第196页)
头文件
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include<iostream>
#include<vector>
#include<string>
using namespace std;
//声明
class Sales_data;
Sales_data add(const Sales_data& s1, const Sales_data& s2); //执行两个Sales_data对象的加法
istream& read(istream& is, Sales_data& item); //将数据从istream读入到Sales_data中
ostream& print(ostream& os, const Sales_data& item); //将Sales_data对象的值输出到ostream
class Sales_data
{
//友元
friend Sales_data add(const Sales_data& s1, const Sales_data& s2);
friend istream& read(istream& is, Sales_data& item);
friend ostream& print(ostream& os, const Sales_data& item);
public:
//构造默认内联函数
Sales_data(const string& s, unsigned un, double re) :bookNo(s), units_sold(un), revenue(un* re) {};
//委托构造函数
Sales_data() :Sales_data(" ", 0, 0) {} //赋一个空,初始化
Sales_data(const string& s) :Sales_data(s, 0, 0) {} //赋予书名字
Sales_data(istream& is) :Sales_data() { read(is, *this); } //委托给了默认构造参数变成了Sales_data(" ", 0, 0),之后再read
Sales_data& combine(const Sales_data& s); //用于将一个Sales_data对象加到另一个对象上,加个引用是为了能够改变原对象,不让其返回临时版本
string isbn() const { return bookNo; } //返回对象的ISBN编号
private:
string bookNo; //书名
unsigned units_sold; //书单价
double revenue; //总售价
inline double avg_price()const; //平均价格,内联函数
};
Sales_data& Sales_data::combine(const Sales_data& s)
{
units_sold += s.units_sold;
revenue += s.revenue;
return *this;
}
Sales_data add(const Sales_data& s1, const Sales_data& s2) //执行两个Sales_data对象的加法
{
Sales_data sum = s1;
sum.units_sold += s2.units_sold;
sum.revenue += s2.revenue;
return sum;
}
istream& read(istream& is, Sales_data& item)
{
double price;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream& print(ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
return os;
}
inline double Sales_data::avg_price()const
{
double price = 0;
price = (units_sold == 0) ? 0 : revenue / units_sold;
return price;
}
#endif // ! SALES_DATA_H
主函数
#include<fstream>
#include<string>
#include<vector>
#include"Sales_data.h"
using namespace std;
int main(int argc, char** argv)
{
ifstream ifs(argv[1]);
if (!ifs) return 1;
Sales_data total(ifs);
if (!total.isbn().empty())
{
Sales_data trans; //当使用 argv 中的实参时, 一定要记得可选的实参从 argv[1] 开始; argv[0] 保存程序的名字, 而非用户输入
while (read(ifs, trans))
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total);
cout << endl;
total = trans;
}
}
print(cout, total);
cout << endl;
return 0;
}
else
{
cerr << "No data?!" << endl;
return -1; // indicate failure
}
}
输出结果:
操作过程:第一步点属性
第二步:点调试后选择文件
文本内容:
8.2.2 文件模式
练习8.7 修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给main函数
#include<fstream>
#include<string>
#include<vector>
#include"Sales_data.h"
using namespace std;
int main(int argc, char** argv)
{
ifstream input(argv[1]); // "../data/book.txt"
ofstream output("E:\\C++练习\\test8\\output.txt"); // "../data/out.txt"
Sales_data total;
if (read(input, total)) {
Sales_data trans;
while (read(input, trans)) {
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(output, total) << endl;
total = trans;
}
}
print(output, total) << endl;
}
else {
cerr << "No data?!" << endl;
}
}
首先我新建一个E:\C++练习\test8\output.txt文档,并且内容如下:
运行程序后:结果存了进去
练习8.8 修改上一题的程序,将结果追加到给定的文件末尾。对用一个输出文件,运行程序至少两次,检验数据是否得到保留
#include<fstream>
#include<string>
#include<vector>
#include"Sales_data.h"
using namespace std;
int main(int argc, char** argv)
{
ifstream input(argv[1]); // "../data/book.txt"
ofstream output("E:\\C++练习\\test8\\output.txt",ofstream::app); // 模式为输出和追加
Sales_data total;
if (read(input, total)) {
Sales_data trans;
while (read(input, trans)) {
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(output, total) << endl;
total = trans;
}
}
print(output, total) << endl;
}
else {
cerr << "No data?!" << endl;
}
}
原先txt内容:
运行一次后:
运行两次后:
8.3 string流
8.3.1 使用istringstream
练习8.9 使用你为8.1.2节(第281页)第一个练习所编写的函数打印一个istringstream对象的内容
#include<sstream>
#include<string>
#include<fstream>
using namespace std;
istream& input(istream& is)
{
string s;
while (is >> s)
cout << s << endl;
is.clear();
return is;
}
int main()
{
istringstream s("aa\nbb\ncc\ndd");
input(s);
}
练习8.10 编写程序,将来自一个文件中的行保存在一个vector中。然后使用一个istringstream从vector读取数据元素,每次读取一个单词。
文档内容:
主程序
#include<string>
#include<vector>
#include<fstream>
#include<sstream>
using namespace std;
int main()
{
ifstream in("E:\\C++练习\\test8\\8.10.txt");
string str;
vector<string> hold;
while (getline(in, str)) {
hold.push_back(str);
}
for (auto i : hold) {
istringstream record(i);
while (record >> str)
cout << str << endl;
}
}
运行结果:
练习8.11 本节的程序在外层while循环中定义了istringstream对象。如果record对象定义在循环之外,你需要对程序进行怎样的修改?
重写程序,将record的定义移到while循环之外,验证你设想的修改方法是否正确。
#include<string>
#include<vector>
#include<fstream>
#include<sstream>
using namespace std;
struct PersonInfo {
string name;
vector<string> phones;
};
int main()
{
string line, word;
vector<PersonInfo>peoples;
istringstream record;
while (getline(cin, line)) {
PersonInfo people;
record.str(line);
record >> people.name;
while (record >> word) {
people.phones.push_back(word);
}
record.clear();
peoples.push_back(people);
}
}
练习8.12 我们为什么没有在PersonInfo中使用类内初始化?
string和vector可以自己初始化,并不会出现未定义的情况。
8.3.2 使用ostringstream
练习8.13 重写本节的电话号码程序,从一个命名文件而非cin读取数据
#include<string>
#include<vector>
#include<fstream>
#include<sstream>
using namespace std;
struct PersonInfo {
string name;
vector<string> phones;
};
bool valid(const string& s)
{
for (const auto c : s)
if (!isdigit(c)) return false;
return true;
}
string format(const string& s)
{
return s;
}
int main()
{
ifstream in("E:\\C++练习\\test8\\phones.txt");
string line, word;
vector<PersonInfo>peoples;
istringstream record;
while (getline(in, line)) {
PersonInfo people;
record.str(line);
record >> people.name;
while (record >> word) {
people.phones.push_back(word);
}
record.clear();
peoples.push_back(people);
}
for (const auto& entry : peoples) {
ostringstream formatted, badNums;
for (const auto& nums : entry.phones) {
if (!valid(nums)) {
badNums<<" "<<nums;
}
else {
formatted << " " << format(nums);
}
}
if (badNums.str().empty())
cout << entry.name << " " << formatted.str() << endl;
else
cerr << "input error:" << entry.name << "invalid numbers(s)" << badNums.str() << endl;
}
}
练习8.14 我们为什么将entry和nums定义为 const auto & ?
它们都是类类型,因此使用引用避免拷贝,提高了效率;在循环当中不会改变它们的值,因此用const。