C++primer学习笔记及作业答案之第八章

这篇博客详细介绍了C++ Primer第八章的内容,包括IO类的使用、文件输入输出操作以及string流的管理。文中强调了IO对象不可拷贝和赋值,以及如何处理文件结束和错误情况。还涵盖了从输入流读取数据、文件模式的设定、字符串流的复用,并给出了多个课后习题的解答和示例程序,涉及从文件读取数据、保存到文件、追加输出以及istringstream对象的运用等实践场景。

笔记:

8.1 IO类

IO对象无拷贝或赋值。因此不能将形参或返回类型设置为流类型,通常以引用方式传递和返回流。且不能是const的。

将流的状态作为条件判断时,如while(cin >> i),当遇到了文件结束符,或者遇到了IO流错误,或者读入了无效数据时,循环便会终止。

当一个输入流被关联到一个输出流时,任何试图从输入读取数据的操作都会先刷新关联的输出流。标准库将cout和cin关联在一起。

8.2 文件输入输出

以out模式打开文件会丢弃已有数据,每次调用open都会确定文件模式。

调用close后才可以diaoyongopen打开另一个文件。

8.3 string流

重复使用字符串流时,每次都要调用clear

 

课后习题:

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

//练习 8.1
#include <iostream>
#include <stdexcept>

using namespace std;

istream & read(istream & is)
{
	int val;
	while (is >> val, !is.eof())  //直到遇见文件结束符才停止读取
	{
		if (is.bad())        //输入流已经奔溃了
		{
			throw runtime_error("IO流错误");
		} 
		if (is.fail())       //输入流发生错误,比如输入了字符,输入double类型也会引发错误,但是仍然能输出
		{ 
			cerr << "数据错误,请重试:" << endl;
			is.clear();
			is.ignore(100, '\n');
			continue;
		}
		cout << val << endl;
	}
	is.clear();
	return is;
}

int main()
{
	cout << "请输入一些整数,按Ctrl+Z结束" << endl;
	read(cin);

	system("pause");
	return 0;
}

练习 8.2:测试函数,调用参数为cin。

答:见上。

练习 8.3:什么情况下,下面的while 循环会终止?

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

答:遇到了文件结束符,或者遇到了IO流错误,或者读入了无效数据。

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

//练习 8.4

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main()
{
	ifstream in("data"); //打开文件
    //相当于检查in的状态
	if (!in)
	{
		//cerr在iostream中定义
		cerr << "无法打开输入文件" << endl;
		return -1;
	}
	string line;
	vector<string> words;
	while (getline(in, line))    //从文件中读取一行
	{
		words.push_back(line);
	}

	in.close();         //关闭文件
	 //遍历输出vector
	vector<string>::const_iterator it = words.begin();
	while (it != words.end())
	{
		cout << *it << endl;
	}

	system("pause");
	return 0;
}

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

//练习 8.4

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main()
{
	ifstream in("data"); //打开文件
    //相当于检查in的状态
	if (!in)
	{
		//cerr在iostream中定义
		cerr << "无法打开输入文件" << endl;
		return -1;
	}
	string line;
	vector<string> words;
	while (in >> line)    //从文件中读取一个单词
	{
		words.push_back(line);
	}

	in.close();         //关闭文件
	 //遍历输出vector
	vector<string>::const_iterator it = words.begin();
	while (it != words.end())
	{
		cout << *it << endl;
	}

	system("pause");
	return 0;
}

练习 8.6:重写7.1.1节的书店程序(第229 页,从一个文件中读取交易记录。将文件名作为一个参数传递给main(参见6.2.5 节,第196 页)。

//练习 8.6

#include <iostream>
#include <fstream>
#include "Sales_data.h"

using namespace std;

//argc表示的是argv中元素的数量,argv[0]是保存程序的名字的而非用户的输入
//因此要从argv[1]开始
int main(int argc, char * argv[])
{
	if (argc != 2)
	{
		cerr << "请给出文件名" << endl;
		return -1;
	}
	ifstream in(argv[1]);
	if (!in)
	{
		cerr << "无法打开文件" << endl;
		return -1;
	}

	Sales_data total;                     //保存当前求和结果的变量
	if (read(in, total))
	{                                          //读入第一笔交易
		Sales_data trans;                 //保存下一笔交易
		do
		{                                      //读入剩余的交易
			if (total.isbn() == trans.isbn())  //检查isbn
				total.combine(trans);          //更新变量total当前的值
			else
			{
				print(cout, total) << endl;    //输出结果
				total = trans;                 //处理下一本书
			}
		} while (read(cin, trans));
		print(cout, total) << endl;            //输出最后一条交易
	}
	else
	{
		cerr << "No data?" << endl;
	}

	system("puase");
	return 0;
}

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

// 练习8.7 
#include <iostream>
#include <fstream>
#include "Sales_data.h"

using namespace std;

//argc表示的是argv中元素的数量,argv[0]是保存程序的名字的而非用户的输入
//因此要从argv[1]开始
int main(int argc, char * argv[])
{
	if (argc != 3)
	{
		cerr << "请给出输入、输出文件名" << endl;
		return -1;
	}
	ifstream in(argv[1]);
	if (!in)
	{
		cerr << "无法打开输入文件" << endl;
		return -1;
	}
	ofstream out(argv[2]);
	if (!out)
	{
		cerr << "无法打开输出文件" << endl;
		return -1;
	}

	Sales_data total;                     //保存当前求和结果的变量
	if (read(in, total))
	{                                          //读入第一笔交易
		Sales_data trans;                 //保存下一笔交易
		if (total.isbn() == trans.isbn())  //检查isbn
			total.combine(trans);          //更新变量total当前的值
		else
		{
			print(out, total) << endl;    //输出结果
			total = trans;                 //处理下一本书
		}
		print(out, total) << endl;        //输出最后一条交易
	} 
		          
	else
	{
		cerr << "No data?" << endl;
	}

	system("pause");
	return 0;
}

这里需要在windows下编译运行程序,g++ -o main mian.cpp生成main.exe文件,然后main.exe 文件名1 文件名2,就可以了。

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

// 练习8.7 
#include <iostream>
#include <fstream>
#include "Sales_data.h"

using namespace std;

//argc表示的是argv中元素的数量,argv[0]是保存程序的名字的而非用户的输入
//因此要从argv[1]开始
int main(int argc, char * argv[])
{
	if (argc != 3)
	{
		cerr << "请给出输入、输出文件名" << endl;
		return -1;
	}
	ifstream in(argv[1]);
	if (!in)
	{
		cerr << "无法打开输入文件" << endl;
		return -1;
	}
	ofstream out(argv[2], ofstream::app);  //以ofstream默认是截断的,除非设置app
	if (!out)
	{
		cerr << "无法打开输出文件" << endl;
		return -1;
	}

	Sales_data total;                     //保存当前求和结果的变量
	if (read(in, total))
	{                                          //读入第一笔交易
		Sales_data trans;                 //保存下一笔交易
		if (total.isbn() == trans.isbn())  //检查isbn
			total.combine(trans);          //更新变量total当前的值
		else
		{
			print(out, total) << endl;    //输出结果
			total = trans;                 //处理下一本书
		}
		print(out, total) << endl;        //输出最后一条交易
	} 
		          
	else
	{
		cerr << "No data?" << endl;
	}

	system("pause");
	return 0;
}

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

//练习 8.9
#include <iostream>
#include <sstream>
#include <string>
#include <stdexcept>

using namespace std;

istream & read(istream & is)
{
	string s;
	while (is >> s)  //这里不能加遇到文件停止符,否则最后一个输出不出来。
	{
		if (is.bad())        //输入流已经奔溃了
		{
			throw runtime_error("IO流错误");
		} 
		if (is.fail())         //
		{ 
			cerr << "数据错误,请重试:" << endl;
			is.clear();
			is.ignore(100, '\n');
			continue;
		}
		cout << s << " ";
	}
	cout << endl;
	is.clear();
	return is;
}

int main()
{
	istringstream iss("C++ Primer 第五版");
	read(iss);

	system("pause");
	return 0;
}

练习 8.10:编写程序,将来自一个文件中的行保存在一个vector<string>中。然后使用一个istringstream 从vector 读取数据元素,每次读取一个单词。

//练习 8.10

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

using namespace std;

int main()
{
	ifstream ifs("Sales_data1.txt");
	if (!ifs)
	{
		cerr << "无法打开文件" << endl;
		return -1;
	}
	string line;
	vector<string> words;
	while (getline(ifs, line))
	{
		words.push_back(line);
	}
	
	ifs.close();      //记得关闭文件
	
	for (auto it = words.begin(); it != words.end(); ++it)
	{
		istringstream iss(*it);
		string word;
		//这里要用循环输出一整行句子
		while (iss >> word)
		{
			cout << word << endl;
		}
	}
	

	system("pause");
	return 0;
}

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

//练习 8.11
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

struct PersonInfo {
	string name;
	vector<string> phones;
};

int main()
{
	string line, word;
	vector<PersonInfo> people;
	istringstream record;
	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);
	}


	system("pause");
	return 0;
}

练习 8.12:我们为什么没有在Personlnfo 中使用类内初始化?

答:由于每个人的电话号数量不固定,因此更好的方式不是通过类内初始化指定人名和所有电话号码,而是在缺省初始化之后,在程序中设置人名并逐个添加电话号码。

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

// 练习 8.13
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

struct PersonInfo
{
	string name;
	vector<string> phones;
};

string format(const string &s) { return s; }

bool valid(const string &s)
{
	return true;
}

int main(int argc, char *argv[])
{
	string line, word;
	vector<PersonInfo> 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);

	}

	ostringstream os;    //逐步构造输出,最后一起打印
	for (const auto &entry : people) 
	{
		ostringstream formatted, badNums;
		for (const auto &nums : entry.phones) 
		{
			if (!valid(nums)) 
			{
				badNums << " " << nums;
			}
			else
				formatted << " " << format(nums);
		}
		if (badNums.str().empty())
			os << entry.name << " " << formatted.str() << endl;
		else
			cerr << "输入错误" << entry.name << "无效号码" << badNums.str() << endl;
	}
	cout << os.str() << endl;

	return 0;
}

练习 8.14:我们为什么将entry 和nums 定义为const auto&?

答:const表示在循环中不会改变这些项的值,使用引用的原因是避免拷贝。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值