前言
简单看一下就行
文章目录
- 一、IO类
- 1.1基本概念
- 1.2管理输出缓冲
- 二、文件输入输出
- 2.1文件模式
- 三、string流
- 3.1istringstream
- 3.2ostringstream
一、IO类
1.1基本概念
我们常见的流有istream和ostream
,这两个流都是有关输入和输出的,此外,下标列举了一些其它的IO类型
。fstream
定义了读写命名文件的类型,sstream
定义了读写内存string
对象的类型。
不同IO
类型之间虽然存在着差异,但是我们在使用时完全可以忽略。可以用>>
读取数据,不管是从控制台窗口用户的输入、磁盘文件中读入还是读取一个字符串。标准库通过继承机制,忽略了这些差异(这部分在15章和18章有谈到,感觉这种知识点目前阶段可以不用去深究,知道怎么用就行)。
IO对象没有拷贝或赋值
IO
对象不能对其进行拷贝或者赋值,所有我们在编写函数时,不能将其作为形参或者返回类型。通常进行IO操作的函数以引用的方式传递和返回流。
IO操作的条件状态
书上列举很多使用IO
操作时可能出现的条件状态,可以把它们理解为一个报错信息提醒。比如定义了一个整型变量,输入的确实一个字符串,这时就会出现IO
操作失败的信息,一旦一个流发生错误,其后面跟着的IO操作都会失败。
确定一个流对象的状态的最简单的方法是将它当做一个条件来使用。
while (cin >> word)
如果我们要查询一个流的具体状态,可以通过iostate
类型,它里面有4个constexpr
值:badbit
表示系统级错误,流无法再使用,比如不可恢复的读写错误;failbit
就是上面提到了输入类型不匹配,这种情况是可以修正的,流可以继续使用;eofbit
表示文件结束位置,这时failbit
也会被置位;goodbit
值为0表示流未发生错误。
1.2管理输出缓冲
每个输出流都管理一个缓冲区,用来保存程序读写的数据。缓冲区机制可以让操作系统将程序的多个输出操作组合成单一的系统级写操作。
刷新输出缓冲区
导致缓冲区刷新的条件有很多,如下:
- 程序正常结束,作为main函数的
return
操作的一部分 - 缓冲区满时,需要刷新才能写入新的数据
- 使用
endl
显式刷新缓冲区,还有flush
和ends
都可以刷新缓冲区 - 每个输出操作之后,可以使用操纵符
unitbuf
设置流的内部状态,来清空缓冲区(使用该操作会在输出后立即刷新缓冲区,适用于程序之后的所有输出,可以通过设置nounitbuf
重置回到正常的缓冲方式) - 两个关联的流在其中一个被读写时,另一个流的缓冲区会被刷新。比如
cin
和cerr
都关联到cout
,当读cin
或者写cerr
都会导致cout
的缓冲区刷新(可以通过tie
构建两个流之间的映射关系)
二、文件输入输出
当要读写一个文件时,可以定义个文件流对象,并将对象与文件关联起来。每个文件流类都定义了一个名为open
的成员函数。
ifstream in(file); // 传入一个文件,open函数会被自动调用
in.close(); // 同样的,当我们销毁一个fstream对象时,close会自动被调用
2.1文件模式
每个流都有一个关联的文件模式。
对于上面的模式对流有一定的限制:
- 只可以对
ofstream
或fstream
对象设定out
模式 - 只可以对
ifstream
或fstream
对象设定in
模式 - 只有当
out
被设定时才可以使用trunc
模式。(即使没有指定trunc
,以out
模式打开的文件也会被截断) - 只要
truc
模式没被设定,就可以设定app
模式。在app模式下。即使没有显式指定out模式,文件也总是以输出方式打开
每个文件流类型都定义了一个默认的文件模式,ifstream
默认以in
模式打开,ofstream
默认以out
打开,fstream
默认以in和out
打开。
在只使用out
时,打开的文件的内容会被清空,所以需要加上app
模式把新的内容添加到文件末尾。
三、string流
3.1istringstream
处理文本或者单个单词可以使用。
struct PersonInfo {string name;vector<string> phone;
}int main() {string line, word;vector<PersonInfo> people;while (getline(cin, line)) { // 读取一行输入PersonInfo info;istringstream record(line); // 把读取的输入存到istringstream流中record >> info.name; // 读取名字while (record >> word) // 读取电话号码 line中含有名字和电话号码,这里意思就是把剩余的文件,也就是电话号码,读取出来用word代替info.phones.push_back(word);people.push_back(info);}
}
3.2ostringstream
用于输出文本等操作。比如上面我们已经读入若干人的姓名和电话号码,需要逐个验证号码的正确性并输出。
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;elsecerr << "Input error:" << entry.name << " invalid number(s) " << badNums.str() << endl;
}