概述
c++不直接处理输入输出,而是通过定义在标准类库中的类来处理IO。这些类支持从设备读取数据,向设备写入数据的IO操作,设备可以是文件、控制台窗口等。还可以从内存IO。
IO类
- iostream:
- istream,wistream
- ostream,wostream
- iostream,wiostream
- ftream:
- iftream, wifstream
- oftream, wofstream
- fstream, wfstream
- sstream:
- istringstream, wistream
- ostringstream, wostringstream
- stringstream, wstringstream
前缀w是处理宽字符的版本。
标准库使我们能够忽略不同流之间的差异,这是通过继承机制实现的。iftream
和istringstream
都继承自istream
对象。类似的ofstream
和ostringstream
都继承自ostream
。
IO对象无拷贝或赋值
在之前的博文中说过,IO对象是不可以拷贝或赋值的。所以我们不能将形参或返回值的类型设置为流类型。进行IO的函数通常以引用的方式传递和返回流。读写IO通常会改变对象的状态,所以传递和返回的引用通常不是const
的。
ofstream out1, out2;
out1 = out2; //错误,不能对流对象赋值
oftream print(ofstream); //错误,不能初始化ofstream参数
out2 = print(out2); //错误,不能拷贝流对象
条件状态
IO操作可能会发生错误,一些错误是可以恢复的。下表列出了IO类定义的一些函数和标志,可以帮助我们访问和操纵流的条件状态。
流状态标志位
状态标志 | 值(二进制) | 说明 |
---|---|---|
goodbit | 000 | 流状态正常,没有错误 |
eofbit | 001 | 到达文件末尾(End Of File) |
failbit | 010 | 发生可恢复错误(如类型不匹配) |
badbit | 100 | 发生严重错误(如流损坏) |
流状态查询函数
函数 | 返回值 | 说明 |
---|---|---|
s.eof() | bool | 如果设置了eofbit 则返回true |
s.fail() | bool | 如果设置了failbit 或badbit 则返回true |
s.bad() | bool | 如果设置了badbit 则返回true |
s.good() | bool | 如果流状态正常(所有错误位未设置)则返回true |
s.rdstate() | iostate | 返回流的当前状态值 |
流状态操作函数
函数 | 参数 | 说明 |
---|---|---|
s.clear() | 无 | 重置所有错误标志,将流状态设置为goodbit |
s.clear(flags) | iostate | 将流状态设置为指定值 |
s.setstate(flags) | iostate | 添加指定的状态标志 |
一个流一旦发生错误,其上后续的IO操纵都会失败。只有当一个流处于无错误的状态时,我们才可以从中读取数据,或向它写入数据。我们应该在使用一个流之前检查它是否处于良好状态。
while (cin >> word)//ok:读操作成功
我们也可以查询流的状态。IO库中定义了一个与机器无关的类型iostate,它提供了表达流状态的完整功能。使用good
、fail
、bad
等函数确定流的错误状态。
通过使用rdstate
、setstate
、clear
来管理条件状态。
接下来给出一个示例,用于从一个istream
对象中读取数据,并将数据打印在控制台中:
#include <iostream>
#include <string>std::istream& readAndPrint(std::istream& is) {std::string word;while (is >> word) { // 读取直到文件结束std::cout << word << " "; // 打印读取的内容}std::cout << std::endl;is.clear(); // 复位流状态return is;
}
管理输出缓冲
每个输出流都管理一个缓冲区,用来保存程序读写的数据。考虑如下代码:
os << "please enter a value: ";
字符串可能不会立即被显示出来,而是被操作系统保存在缓冲区中,随后再打印。有了缓冲区机制,操作系统就可以把多个输出操作合并为单一的系统极写操作,这将极大地提升程序地性能。
缓冲区刷新指的是数据真正地写入到设备或文件中,导致缓冲区刷新地原因有很多:
- 程序正常结束,main中地return语句被执行,导致缓冲区刷新。
- 缓冲区满
- 使用endl显式刷新缓冲区
- 使用unitbuf设置流的内部状态,以此来清空缓冲区。
- 一个输出流可能关联到另一个流。当读写被关联的流时,关联到的流的缓冲区会被刷新。
示例代码如下所示:
#include <iostream>
#include <fstream>
#include <unistd.h> // 用于 sleep 函数// 1. 程序正常结束导致缓冲区刷新
void normalTermination() {std::cout << "\n1. 程序正常结束刷新示例:\n";std::cout << "这条消息会在程序结束时自动刷新";// 无显式刷新,依靠main函数return刷新
}// 2. 缓冲区满导致自动刷新
void bufferFull() {std::cout << "\n2. 缓冲区满自动刷新示例:\n";// 通常缓冲区大小约为4KB,这里用循环填满for (int i = 0; i < 1000; ++i) {std::cout << "填充缓冲区数据 " << i << " ";}std::cout << "\n缓冲区已满并自动刷新\n";
}// 3. 使用endl显式刷新
void explicitFlush() {std::cout << "\n3. 使用endl显式刷新示例:\n";std::cout << "这条消息会立即显示" << std::endl;std::cout << "下一条消息" << std::endl;
}// 4. 使用unitbuf设置流状态
void unitbufExample() {std::cout << "\n4. unitbuf设置示例:\n";std::cout << std::unitbuf; // 开启每次操作后自动刷新std::cout << "unitbuf模式 - 每次输出后自动刷新";sleep(1);std::cout << " 即使没有换行也会刷新";std::cout << std::nounitbuf; // 关闭自动刷新std::cout << "\n恢复普通模式\n";
}// 5. 关联流导致的刷新
void tiedStreams() {std::cout << "\n5. 关联流刷新示例:\n";std::ofstream logfile("log.txt");// 将cout与logfile关联std::cout.tie(&logfile);std::cout << "向cout写入会刷新logfile: ";logfile << "这条消息会立即写入文件\n";// 请求输入也会刷新关联的coutstd::cout << "请输入一个数字: ";int num;std::cin >> num;std::cout << "你输入了: " << num << std::endl;// 恢复默认关联std::cout.tie(nullptr);
}int main() {normalTermination();bufferFull();explicitFlush();unitbufExample();tiedStreams();// 程序结束时会自动刷新所有缓冲区return 0;
}