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

来源:互联网 发布:love rosie知乎 编辑:程序博客网 时间:2024/06/05 00:50

今天是第八篇笔记,主要总结的内容是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 /ifstreamsstream / istringstream /ostringstream</span>
其中C++为了支持宽字符类型,又定义了对应的宽字符版本类型,比如你可能看见如下的:
<span style="font-size:18px;">wostreamwcinwcout...</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;elsecout << "The stream is not valid " << endl;cout << "Test the stream after clearing " << endl;cin.clear();if (cin)cout << "The stream is valid " << endl;elsecout << "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;elsecout << "The stream is not valid " << endl;cout << "Test the stream after clearing " << endl;cin.clear();if (cin)cout << "The stream is valid " << endl;elsecout << "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



0 0