您的位置:首页 > 编程语言 > C语言/C++

C++ Primer 笔记+习题解答(八)

2015-02-03 21:38 344 查看
今天是第八篇笔记,主要总结的内容是C++标准库中的第一部分之IO库。最近几天因为一些环境干扰,更新的速度着实变慢。其实对小白来说,学习过程中比较可怕的一件事就是信息负载,故今日贴在博文首页,警醒自己。大致估计下日程安排。从2.4号起到除夕2.18号,共计14天。C++ Primer一共十八章,算上附录是19个章节。预计大年初二左右结束这本书的学习。节奏应该是一天看书,一天写博文总结。进入正题。
若存在错误 请指正 谢谢

0.引言:

1.C++ 不存在直接处理输入输出的机制,而是转交给标准库中的类来处理IO。C++的IO机制支持从各种设备读写数据。这个地方的设备要好好理解下。设备可以指的是控制台窗口,命名文件,内存IO。

2.IO类型定义了内置数据类型的读写操作,不然形如这样的语句:

<span style="font-size:18px;">int var=0;
cin>>var;
cout<<var;</span>
我刚学C++时候就特别好奇为什么可以这个样子搞,当时眼界阅历有限,虽然翻了不少书,但是大多数教材都是闭口不提标准库,对我而言着实坑爹。

我们已经接触到IO设施:
<span style="font-size:18px;">cout / cin /cerr />> /<< /getline /iostream </span>


3.下面会介绍一些新知识。

目前我们使用的IO类型都是关联到控制台窗口的,所以你会在屏幕上看见输入输出。但是程序的需求绝不仅限于此。

相关头文件:

<span style="font-size:18px;">iostream/ ostream /istream 
fstream / ofstream /ifstream
sstream / istringstream /ostringstream</span>
其中C++为了支持宽字符类型,又定义了对应的宽字符版本类型,比如你可能看见如下的:
<span style="font-size:18px;">wostream
wcin
wcout
...</span>
无需奇怪,两个版本之前用法无差异。

由于IO类型之间存在继承关系,所以把继承对象当作基类对象使用。

1.IO对象无拷贝或赋值:

1.由于无法拷贝和赋值,因此不能将形参和返回类型设为流类型,而是要设置成引用类型。因为读写流会改变流的状态,所以IO对象绝不会是const的。

2.相关状态量和函数:

<span style="font-size:18px;color:#330099;">iostate/goodbit/failbit/eofbit/badbit
上面的量是一些状态量,其iostate总合。
相关函数:
eof() /fail()/good()/bad()/clear()/clear(flags)/setstate(flags)/rdstate()</span>
具体的函数意思翻书或者谷歌就知晓了。
在使用对象的时候,最好检测下流的状态,比如我们常见的:

<span style="font-size:18px;color:#330099;">while(cin>>word)</span>
我们已经学过运算符了,>>运算符返回的cin对象,所以实际检测的是对象的状态。
我们讲流(对象)作为条件使用,只能知晓其是否出错,但是一旦出错我们不知道具体错误的,这个时候就可以通过函数检测。

<span style="font-size:18px;color:#330099;">不可恢复的错误:badbit 被置位。
可修复的错误:failbit  被置位。
</span>
当遇到文件结束符的时候,failbit和eofbit会被置位。一旦eofbit/failbit/badbit 任何一个被置位了,检测流状态得到布尔值是false。

3.缓冲相关:

每个流都管理一个缓冲区,用了保存数据。如下的行为会导致缓冲刷新:

1.程序正常结束

2.缓冲区满。

3.输出控制符,如endl/ends/flush等。

4.在每个输出操作之后用unitbuf设置流内部状态。

5.流之间的相互关联。

相关解释:

1.刷新输出缓冲区:

<span style="font-size:18px;color:#330099;">cout<<endl; 刷新缓冲并且换行。
cout<flush; 刷新缓冲。
cout<<ends; 附加一个空字符然后刷新缓冲区。</span>

2.unitbuf操纵符:

每次输出操作之后都会刷新缓冲区,行为是类似flush,不会有附加内容。一般来说unitbuf同cerr关系比较近,所以cerr是立即输出不缓冲的。5e

<span style="font-size:18px;color:#330099;">cout<<unitbuf;
//每次输出后都会刷新缓冲。
恢复正常缓冲刷新机制:
cout<<nonunitbuf;</span>

3.缓冲去的一些补充知识,我就做个搬运工了哈。

缓冲去相关知识链接

其中注意到一点,缓冲区的几个分类:

全缓冲/行缓冲/无缓冲。具体内容可以翻看我搬运的博文,而且他们总结的比我好多了。

4.关联输入流和输出流:

执行读取操作会导致输入缓冲区被刷新,也就是cin会刷新cout.

相关函数:

1.无形参tie函数:返回指向调用函数对象所关联对象的指针,如调用函数的对象不不存在关联,那么返回的是一个空指针。

<span style="font-size:18px;color:#330099;">cin.tie();</span>
<span style="font-size:18px;color:#330099;">正常情况下,cin和cout关联,所以会返回一个指向cout的指针。</span>

2.有参数tie函数:将调用的对象同参数的对象关联起来。

<span style="font-size:18px;color:#330099;">cin.tie(&cerr);</span>
<span style="font-size:18px;color:#330099;">将cin关联到cerr.不被推荐的做法,因为cin应该同cout关联。</span>

tips:关联之间存在一种类似函数的关系,可以多对一,不可以一对多。意思就是多个对象可以关联到同一个ostream对象,但是一个对象不能被关联到多个ostream对象。

2.文件输入输出:

由于继承关系的存在,前面介绍的一些状态量和函数都是可以继续使用的,补充一下新增的函数:

<span style="font-size:18px;">open/close/is_open</span>
<span style="font-size:18px;color:#330099;">三个函数是新增的,具体含义就不写了。</span>


1.ifstream in(file) file是文件名,可以是string 字符串或者c风格字符串。file是c风格字符串时记得一定要以空字符结尾。

2.可以先定义一个对象,然后调用open函数同具体文件绑定起来。如果open失败,failbit会被置位,尽量设置检测措施。

3.当一个对象被销毁时,会自动调用其close函数,斩断之间的关联。

4.文件模式:

in/out/app/ate/trunc/binary

5.以写模式打开的文件会丢失已有的数据:

解决措施是以app或着in模式打开文件。

ofstream out(file)ofstream out(file,ofstream::out)//丢失数据ofstream out(file,ofstream::out|ofstream::app)

3.string 流:

三个头文件:

<span style="font-size:18px;">istringstream/ostingstream/stringstream</span>


新增操作:

<span style="font-size:18px;">strm.str()//返回strm绑定的字符串拷贝。
strm.str(s)//把s拷贝到stream中。</span>

4.总结:

iostream 处理控制台IO;

fstream 处理命名文件IO;

sstream 处理string IO .

5.习题解答:

8.1

<span style="font-size:18px;">#include <iostream>
using namespace std;
istream& read(istream& cin){
	int var = 0;
	while (cin>>var){
		cout << var << " ";
	}
	cout << "Test the stream before clearing " << endl;
	if (cin)
		cout << "The stream is valid " << endl;
	else
		cout << "The stream is not valid " << endl;
	cout << "Test the stream after clearing " << endl;
	cin.clear();
	if (cin)
		cout << "The stream is valid " << endl;
	else
		cout << "The stream is not valid " << endl;
	return cin;
}
int main(){
	read(cin);
	system("pause");
	return 0;
}</span>
8.2

参见8.1两次测试。

8.3

<span style="font-size:18px;">遇到错误的输入,比如i是int型的,你输入一个字符,那么流的状态就失效。
或者当你键入文件结束标识的时候,流的状态也会失效。
流的状态为假后,循环就会终止。一般来说eofbit /badbit/failbit 三者任何一个被置位都会导致循环结束。
</span>
8.4
<span style="font-size:18px;">#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main(){
	ofstream out;
	out.open("test.txt");
	out << "hello world !!!"<<endl;
	out << "You are so beautiful " << endl;
	out.close();
	ifstream in("test.txt");
	string word;
	vector<string> svec;
	while (!in.eof()){
		getline(in, word);
		svec.push_back(word);
	}
	for (auto x : svec)
		cout << x << " ";
	cout << endl;
	system("pause");
	return 0;
}</span>
8.5
<span style="font-size:18px;">#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main(){
	ofstream out;
	out.open("test.txt");
	out << "hello world !!!"<<endl;
	out << "You are so beautiful " << endl;
	out.close();
	ifstream in("test.txt");
	string word;
	vector<string> svec;
	while (!in.eof()){
		//getline(in, word);
		in >> word;//可以上下对比下。
		svec.push_back(word);
	}
	for (auto x : svec)
		cout << x;
	cout << endl;
	system("pause");
	return 0;
}</span>

8.6 8.7 8.8

具体的答案就不写了,我写一下如何从命令行编译运行程序以及如何向命令行传入参数。我看的书都直接一笔带过,导致我根本不会从命令行编译程序。这个东西就是很基础的知识,但是写书的人都默认我们会了,所以正计算机三观太有必要了。

如何从命令行编译运行程序以及向main函数传递参数。
这个方法适用于Visual Studio 2013,其余平台未测试过。
首先在vs 2013 中找到工具一栏,点开可以发现倒数几行有一个是 Visual Studion 命令提示。
点击它我们会发现弹出一个CMD命令行窗口。
接下来是先创建一个文件或者打开一个已经存在的文件。注意一定要加上notepad 关键字。格式是: notepad test.cpp
其中test可以换成自己想要建立的程序名。接着会弹出一个窗口,如果test未创建过,那么会提示你建立一个记事本文件。
你可以在记事本文件里面输入自己的源代码,然后点击文件菜单,保存,退出。
接下来就是编译阶段:
首先输入:
cl /EHsc test.cpp
然后你会在命令行窗口看见生成的可执行文件。
这个时候如果你想要向main函数传递参数,那么输入:
test 1 2 3 //后面的1 2 3 是我们传递给main的参数。你可以输出argc测试下。应该输出数字4.argv[0]指的是程序的名字。
如果你只是想单词的执行程序,那么直接输入:
test.exe 即可。
ps:像main函数传入参数的前提是你的main函数要有形参列表。
暂时介绍到这个地方,我也是初次接触。感兴趣的话可以直接搜索关键字,已经有前辈们替我们总结过了。

8.9

#include <iostream>
#include <string>
#include<sstream>
using namespace std;
istream& read(istream& cin){
	string var;
	while (cin>>var){
		cout << var << " ";
	}
	cout << endl;
	cout << "Test the stream before clearing " << endl;
	if (cin)
		cout << "The stream is valid " << endl;
	else
		cout << "The stream is not valid " << endl;
	cout << "Test the stream after clearing " << endl;
	cin.clear();
	if (cin)
		cout << "The stream is valid " << endl;
	else
		cout << "The stream is not valid " << endl;
	return cin;
}
int main(){
	string s = "Hello World ";
	istringstream is(s);
	read(is);
	system("pause");
	return 0;
}
//这个地方就体现了继承关系。我的形参是istream&类型,但是我传递的是一个isstringstream类型。
正好符号上面我们提到的子类当作父类用。
8.10

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
int main(){
	ofstream fout("temp.txt");  //打开一个文件,写点东西进去。
	fout << "Hello Word " << endl;
	fout << "You are so beautiful " << endl;
	fout.close();//关闭这个文件。并且用读的方式再次打开,写的方式再次打开会丢失数据。
	ifstream fin("temp.txt");
	string line;
	vector<string> svec;
	while (!fin.eof()){
		getline(fin, line);
		svec.push_back(line); //按行存进vector 中。
	}
	string word;
	for (auto x : svec){
		istringstream istirngin(x);
		while (istirngin >> word)
			cout << word<<" ";
		cout << endl;
	}
	cout << endl;
	system("pause");
	return 0;
}
8.11

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
int main(){
	ofstream fout("temp.txt");  //打开一个文件,写点东西进去。
	fout << "You are so beautiful " << endl;
	fout << "Hello Word " << endl;
	fout.close();//关闭这个文件。并且用读的方式再次打开,写的方式再次打开会丢失数据。
	ifstream fin("temp.txt");
	string line;
	vector<string> svec;
	while (!fin.eof()){
		getline(fin, line);
		svec.push_back(line); //按行存进vector 中。
	}
	string word;
	istringstream istirngin;  //在体外定义对象。
	for (auto x:svec){
		istirngin.str(x);  //体内采用自己函数进行绑定。
		while (istirngin >> word)
			cout << word;
		cout << endl;
		istirngin.clear(); //因为用while循环读取,所以最后eofbit被置位。故要清空流,我一开始没注意到这个问题。
	}
	cout << endl;
	system("pause");
	return 0;
}
体外的写法可以参考上面的。记得复位流的状态。

8.12

因为string同vector<stirng>可以顺利地被合成的默认构造函数初始化,无须多此一举。
8.13

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
struct PersonInfo{
	string name;
	vector<string> phone;
};
//定义的结构体用了保存员工信息。
int main(){
	string line, word;
	vector<PersonInfo> People;
	ofstream fout("information");//因为并没有写好的信息文件,所以只能在程序当成写。
	fout << "John 1234567 2345678" << endl;
	fout << "Max 6789090 12343890" << endl;
	fout.close();
	ifstream fin("information");
	while (!fin.eof()){
		getline(fin, line);
		PersonInfo temp;
		istringstream istringin(line);//同line 绑定到一起。
		istringin >>temp.name;//保存姓名。
		while (istringin>>word){
			temp.phone.push_back(word);
		}
		People.push_back(temp);
	}
	system("pause");
	return 0;
}
8.14

避免了拷贝同时也防止了修改。


后记:

昨天晚上本打算一口气写玩的,但是等到写的时候突然泄气了,一股自卑感油然而生,心里总是想着你写不出来的,放弃吧。所以当时我选择了休息,第二天继续写,果然今天的状态好了很多。

End

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: