欢迎关注WX公众号:【程序员管小亮】
专栏C++学习笔记
《C++ Primer》学习笔记/习题答案 总目录
- https://blog.csdn.net/TeFuirnever/article/details/100700212
——————————————————————————————————————————————————————
- 《C++ Primer》习题参考答案:第8章 - 标准 IO 库
文章目录
- 欢迎关注WX公众号:【程序员管小亮】
- 专栏C++学习笔记
- ???????? [Cpp-Prime5 + Cpp-Primer-Plus6 源代码和课后题](https://github.com/TeFuirnever/Cpp-Primer-Plus-Plus-Plus)
- 标准 IO 库
- 1、IO类
- 1)IO象无拷贝或赋值
- 2)条件状态
- 3)管理输出缓冲
- 2、文件输入输出
- 1)使用文件流对象
- 2)文件模式
- 3、string流
- 1)使用istringstream
- 2)使用ostringstream
- 参考文章
???????? Cpp-Prime5 + Cpp-Primer-Plus6 源代码和课后题
标准 IO 库
部分IO库设施:
istream
:输入流类型,提供输入操作。ostream
:输出流类型,提供输出操作。cin
:istream
对象,从标准输入读取数据。cout
:ostream
对象,向标准输出写入数据。cerr
:ostream
对象,向标准错误写入数据。>>
运算符:从istream
对象读取输入数据。<<
运算符:向ostream
对象写入输出数据。getline
函数:从istream
对象读取一行数据,写入string
对象。
1、IO类
头文件 iostream 定义了用于读写流的基本类型,fstream 定义了读写命名文件的类型,sstream 定义了读写内存中 string
对象的类型。
宽字符版本的IO类型和函数的名字以 w
开始,如 wcin
、wcout
和 wcerr
分别对应 cin
、cout
和cerr
。它们与其对应的普通 char
版本都定义在同一个头文件中,如头文件 fstream 定义了 ifstream
和 wifstream
类型。
通常可以将一个派生类(继承类)对象当作其基类(所继承的类)对象来使用,这是通过 继承机制(inheritance) 实现的。
1)IO象无拷贝或赋值
不能拷贝或对IO对象赋值。
1
2
3
4
5ofstream out1, out2; out1 = out2; // 错误:不能对流对象赋值 ofstream print(ofstream); // 错误:不能初始化ofstream参数 out2 = print(out2); // 错误:不能拷贝流对象
由于IO对象不能拷贝,因此不能将函数形参或返回类型定义为流类型。进行IO操作的函数通常以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用不能是 const
的。
2)条件状态
IO操作一个与生俱来的问题是可能发生错误。一些错误是可恢复的,而其他错误则发生在系统深处,已经超出了应用程序可以修正的范围。
IO库条件状态:
1
2
3while (cin >> word) // ok: 读操作成功....
badbit
表示系统级错误,如不可恢复的读写错误。通常情况下,一旦 badbit
被置位,流就无法继续使用了。在发生可恢复错误后,failbit
会被置位,如期望读取数值却读出一个字符。如果到达文件结束位置,eofbit
和 failbit
都会被置位。如果流未发生错误,则 goodbit
的值为0。如果 badbit
、failbit
和 eofbit
任何一个被置位,检测流状态的条件都会失败。
good
函数在所有错误均未置位时返回 true
。而 bad
、fail
和 eof
函数在对应错误位被置位时返回 true
。此外,在 badbit
被置位时,fail
函数也会返回 true
。因此应该使用 good
或 fail
函数确定流的总体状态,eof
和 bad
只能检测特定错误。
流对象的 rdstate
成员返回一个 iostate
值,表示流的当前状态。setstate
成员用于将指定条件置位(叠加原始流状态)。clear
成员的无参版本清除所有错误标志;含参版本接受一个 iostate
值,用于设置流的新状态(覆盖原始流状态)。
1
2
3
4
5
6// 记住cin的当前状态 auto old_state = cin.rdstate(); // 记住cin的当前状态 cin.clear(); // 使cin有效 process_input(cin); // 使用cin cin.setstate(old_state); // 将cin置为原有状态
3)管理输出缓冲
每个输出流都管理一个缓冲区,用于保存程序读写的数据。
导致缓冲刷新(即数据真正写入输出设备或文件)的原因有很多:
- 程序正常结束。
- 缓冲区已满。
- 使用操纵符(如
endl
)显式刷新缓冲区。 - 在每个输出操作之后,可以用
unitbuf
操纵符设置流的内部状态,从而清空缓冲区。默认情况下,对cerr
是设置unitbuf
的,因此写到cerr
的内容都是立即刷新的。 - 一个输出流可以被关联到另一个流。这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。默认情况下,
cin
和cerr
都关联到cout
,因此,读cin
或写cerr
都会刷新cout
的缓冲区。
flush
操纵符刷新缓冲区,但不输出任何额外字符。ends
向缓冲区插入一个空字符,然后刷新缓冲区。
1
2
3
4cout << "hi!" << endl; // 输出hi和一个换行,然后刷新缓冲区 cout << "hi!" << flush; // 输出hi,然后刷新缓冲区,不附加任何额外字符 cout << "hi!" << ends; // 输出hi和一个空字符,然后刷新缓冲区
如果想在每次输出操作后都刷新缓冲区,可以使用 unitbuf
操纵符。它令流在接下来的每次写操作后都进行一次 flush
操作。而 nounitbuf
操纵符则使流恢复使用正常的缓冲区刷新机制。
1
2
3
4cout << unitbuf; // 所有输出操作后都会立即刷新缓冲区 // 任何输出都立即刷新,无缓冲 cout << nounitbuf; // 回到正常的缓冲方式
如果程序异常终止,输出缓冲区是不会被刷新的。当一个程序崩溃后, 它所输出的数据很可能停留在输出缓冲区中等待打印。
当调试一个已经崩溃的程序时,需要确认那些你认为已经输出的数据确实已经刷新了。否则, 可能将大量时间浪费在追踪代码为什么没有执行上,而实际上代码已经执行了,只是程序崩溃后缓冲区没有被刷新,输出数据被挂起没有打印而已。
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。标准库将cout
和cin
关联在一起,因此下面的语句会导致cout
的缓冲区被刷新:
1
2cin >> ival;
交互式系统通常应该关联输入流和输出流。这意味着包括用户提示信息在内的所有输出,都会在读操作之前被打印出来。
使用 tie
函数可以关联两个流。它有两个重载版本:无参版本返回指向输出流的指针。如果本对象已关联到一个输出流,则返回的就是指向这个流的指针,否则返回空指针。tie
的第二个版本接受一个指向 ostream
的指针,将本对象关联到此 ostream
。
1
2
3
4
5
6
7cin.tie(&cout); // 仅仅是用来展示:标准库将cin和cout关联在一起 // old tie指向当前关联到cin的流(如果有的话) ostream *old_tie = cin.tie(nullptr); // cin不再与其他流关联 // 将cin与cerr关联;这不是一个好主意,因为cin应该关联到cout cin.tie(&cerr); // 读取cin会刷新 cerr而不是cout cin.tie(old_tie); // 重建cin和cout间的正常关联
每个流同时最多关联一个流,但多个流可以同时关联同一个 ostream
。向 tie
传递空指针可以解开流的关联。
2、文件输入输出
头文件 fstream 定义了三个类型来支持文件IO:ifstream
从给定文件读取数据,ofstream
向指定文件写入数据,fstream
可以同时读写指定文件。
1)使用文件流对象
每个文件流类型都定义了open
函数,它完成一些系统操作,定位指定文件,并视情况打开为读或写模式。
创建文件流对象时,如果提供了文件名(可选),open
会被自动调用。
1
2
3ifstream in(ifile); // 构造一个ifstream并打开给定文件 ofstream out; // 输出文件流未关联到任何文件
在C++11中,文件流对象的文件名可以是 string
对象或C风格字符数组。旧版本的标准库只支持C风格字符数组。
在要求使用基类对象的地方,可以用继承类型的对象代替。因此一个接受 iostream
类型引用或指针参数的函数,可以用对应的 fstream
类型来调用。
可以先定义空文件流对象,再调用 open
函数将其与指定文件关联。如果 open
调用失败,failbit
会被置位。
一旦一个文件流已经打开,它就保持与对应文件的关联。对一个已经打开的文件流调用 open
会失败,并导致 failbit
被置位。随后试图使用文件流的操作都会失败。如果想将文件流关联到另一个文件,必须先调用 close
关闭当前文件,再调用 clear
重置流的条件状态(close
不会重置流的条件状态)。
当
fstream
对象被销毁时,close
会自动被调用。
2)文件模式
每个流都有一个关联的文件模式,用来指出如何使用文件。
指定文件模式有如下限制:
- 只能对
ofstream
或fstream
对象设定out
模式。 - 只能对
ifstream
或fstream
对象设定in
模式。 - 只有当
out
被设定时才能设定trunc
模式。 - 只要
trunc
没被设定,就能设定app
模式。在app
模式下,即使没有设定out
模式,文件也是以输出方式打开。 - 默认情况下,即使没有设定
trunc
,以out
模式打开的文件也会被截断。如果想保留以out
模式打开的文件内容,就必须同时设定app
模式,这会将数据追加写到文件末尾;或者同时设定in
模式,即同时进行读写操作。 ate
和binary
模式可用于任何类型的文件流对象,并可以和其他任何模式组合使用。- 与
ifstream
对象关联的文件默认以in
模式打开,与ofstream
对象关联的文件默认以out
模式打开,与fstream
对象关联的文件默认以in
和out
模式打开。
每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。与 ifstream
关联的文件默认以 in
模式打开;与 ofstream
关联的文件默认以 out
模式打开;与 fstream
关联的文件默认以 in
和 out
模式打开。
默认情况下,打开 ofstream
对象时,文件内容会被丢弃,阻止一个 ofstream
清空给定文件内容的方法是同时指定 app
模式:
流对象每次打开文件时都可以改变其文件模式。
1
2
3
4
5
6ofstream out; // 未指定文件打开模式 out.open("scratchpad"); // 模式隐含设置为输出和截断 out.close(); // 关闭out,以便我们将其用于其他文件 out.open("precious", ofstream::app); // 模式为out和app out.close();
保留被
ofstream
打开的文件中已有数据的唯一方法是显式指定app
或in
模式。
3、string流
头文件 sstream 定义了三个类型来支持内存IO:istringstream
从 string
读取数据,ostringstream
向 string
写入数据,stringstream
可以同时读写 string
的数据。
1)使用istringstream
当某些工作是对整行文本进行处理, 而其他一些工作是处理行内的单个单词时,通常可以使用 istringstream
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 成员默认为公有 struct PersonInfo { string name; vector<string> phones; }; string line, word; // 分别保存来自输入的一行和单词 vector<PersonInfo> people; // 保存来自输入的所有记录 // 逐行从输入读取数据,直至cin遇到文件尾(或其他错误) while (getline(cin, line)) { PersonInfo info; // 创建一个保存此记录数据的对象 istringstream record(line); // 将记录绑定到刚读入的行 record >> info.name; // 读取名字 while (record >> word) // 读取电话号码 info.phones.push_back(word); // 保持它们 people.push_back(info); // 将此记录追加到people末尾 }
2)使用ostringstream
当逐步构造输出, 希望最后一起打印时, ostringstream
是很有用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23for (const auto &entry : people) { // 对people中每一项 ostringstream formatted, badNums; // 每个循环步创建的对象 for (const auto &nums : entry.phones) { // 对每个数 if (!valid(nums)) { badNums << " " << nums; // 将数的字符串形式存入badNums } else // 将格式化的字符串"写入" formatted << " " << format(nums); } if (badNums.str().empty()) // 没有错误的数 os << entry.name << " " // 打印名字 << formatted.str() << endl; // 和格式化的数 else // 否则,打印名字和错误的数 cerr << "input error: " << entry.name << " invalid number(s) " << badNums.str() << endl; }
参考文章
- 《C++ Primer》
最后
以上就是高挑花卷最近收集整理的关于《C++ Primer》学习笔记(八):标准 IO 库欢迎关注WX公众号:【程序员管小亮】???????? Cpp-Prime5 + Cpp-Primer-Plus6 源代码和课后题标准 IO 库的全部内容,更多相关《C++内容请搜索靠谱客的其他文章。
发表评论 取消回复