本文把复杂的基类和派生类的作用和关系捋出来,具体的接口请参考相关文档
C++的 I/O 流相关的类,继承关系如下图所示
https://zh.cppreference.com/w/cpp/io
I / O 的概念:内存和外设进行数据交互称为 I / O ,例如:把数据写入磁盘,把数据显示到屏幕,把键盘的数据传到内存等等。
流的概念:可以理解为河水,有源头,有目的,按字节流动。按流传输时,不关心内容,格式,类型等等。
我们重点要掌握的是:输入输出流定义的全局对象 cout cin 等, 还有文件输入输出流,熟悉文件输入输出流相关接口。
成员变量
https://zh.cppreference.com/w/cpp/io/ios_base
我们要熟悉一下 ios_base 中维护的一些成员变量,首先就是流的打开方式
常量 | 解释 |
std::ios::app | 每次写入前寻位到流结尾 |
std::ios::binary | 以二进制模式打开 |
std::ios::in | 为读打开 |
std::ios::out | 为写打开 |
std::ios::trunc | 在打开时舍弃流的内容 |
std::ios::ate | 打开后立即寻位到流结尾 |
std::ios::noreplace | 以独占模式打开 |
这里是不是有些奇怪,怎么打开流像打开文件一样?
以 Linux 系统为例,文件管理模块会把硬件设备全部抽象成文件:显示器,键盘,网卡等属于字符设备文件,存磁盘,u盘等属于块设备文件。经过虚拟文件系统对具体文件系统抽象后,上层会以统一的视角看待底层设备。
所以,这里的一层理解是:所谓的基于流的 I/O 可以理解为进程对某个文件的输入输出。比如,C++中最常用的 cin, cout, 就是对进程的0号文件描述符和1号文件描述符进行操作
文件有自己的打开方式,对应的,流也就有打开方式
视角再拉回来,ios_base 中的成员变量还有寻位相关的
常量 | 解释 |
std::ios::beg | 流的开始 |
std::ios::end | 流的结尾 |
std::ios::cur | 流位置指示器的当前位置 |
还有流的状态
常量 | 解释 |
goodbit | 无错误 |
badbit | 不可恢复的流错误 |
failbit | 输入/输出操作失败(格式化或提取错误) |
eofbit | 关联的输出序列已抵达文件尾 |
输入输出操作
std::basic_streambuf 是输入输出操作的缓冲区
https://zh.cppreference.com/w/cpp/io/basic_streambuf
std::basic_streambuf 关联的缓冲区有两类:
1. 通过操作系统的API访问的实体(文件、TCP 套接字、串行端口、其他字符设备)
2.能解读成缓冲区的对象(std::vector, std::string等)
对于输入操作来说,该缓冲区称为获取区(进程从获取区拿数据)
对于输出操作来说,该缓冲区称为放置区(进程把数据放到放置区)
std::basic_ostream 提供输出操作
https://zh.cppreference.com/w/cpp/io/basic_ostream
标准库提供六个全局 basic_ostream
对象:
在标头 | |
cout | wcout | 写入到标准 C 输出流 stdout (全局对象) |
cerr | wcerr | 写入到标准 C 错误流 stderr,无缓冲 (全局对象) |
clog | wclog | 写入到标准 C 错误流 stderr (全局对象) |
std::basic_istream 提供输入操作
https://zh.cppreference.com/w/cpp/io/basic_istream
标准库提供两个全局 basic_istream
对象:
在标头 | |
cin | wcin | 从标准 C 输入流 stdin 读取 (全局对象) |
std::basic_iostream 继承了std::basic_ostream 和 std::basic_istream ,提供输入输出操作。
https://zh.cppreference.com/w/cpp/io/basic_iostream
为了理解 std::basic_streambuf std::basic_ostream std::basic_istream 我们看如下程序,下面程序中 MyStreamBuf 是自定义的缓冲区, 封装 std::string 对象。MyStreamBuf 继承了 std::streambuf 可以重写 std::streambuf 的虚函数,std::ostream 接受一个缓冲区 MyStreamBuf 作为参数实例化对象。
注:std::ostream , std::streambuf 是 std::basic_ostream , std::basic_streambuf 的别名
下面封装缓冲区是 重写 overflow 和 xsputn 两个虚函数,可以参考std::streambuf文档
#include <iostream>
#include <streambuf>
#include <string>// 自定义流缓冲区:将输出内容存储到字符串
class MyStreamBuf : public std::streambuf {
protected:std::string buffer_;// 处理单个字符输出virtual int_type overflow(int_type c) override {if (c != traits_type::eof()) {buffer_ += traits_type::to_char_type(c);}return c;}// 处理多字符输出virtual std::streamsize xsputn(const char* s, std::streamsize n) override {buffer_.append(s, n);return n;}public:const std::string& getBuffer() const { return buffer_; }
};int main() {MyStreamBuf buf; // 实例化自定义流缓冲区std::ostream myStream(&buf); // 构造ostream,使用自定义缓冲区myStream << "Hello World! " << 42 << std::endl; // 输出到流// 获取并打印缓冲区内容std::cout << "输出流里的内容是: " << buf.getBuffer();return 0;
}
文件的输入输出流
下面我们介绍 std::basic_fstream 。 std::basic_fstream 继承关系如下所示
基类我们前文已经介绍了,ios_base 维护必要的变量, basic_istream 提供输入操作, basic_ostream 提供输出操作。
std::basic_fstream 作为派生类,添加了一些文件相关的接口,可以参考文档
https://zh.cppreference.com/w/cpp/io/basic_fstream
下面写一个小程序,可以拷贝图片或视频
#include <iostream>
#include <fstream>
using namespace std;
int main() {// 打开源文件(二进制模式)fstream inFile("C:\\Users\\34497\\Desktop\\屏幕录制 2025-01-15 213832.mp4", ios::in | ios::binary); if (!inFile) {cerr << "无法打开源文件" << endl;return 1;}// 创建目标文件(二进制模式并清空内容)fstream outFile("C:\\Users\\34497\\Desktop\\destination.mp4", ios::out | ios::binary | ios::trunc); if (!outFile) {cerr << "无法创建目标文件" << endl;inFile.close();return 1;}// 使用缓冲区提高读写效率const int BUFFER_SIZE = 4096;char buffer[BUFFER_SIZE];// 循环读写文件内容while (inFile.read(buffer, BUFFER_SIZE)) {outFile.write(buffer, inFile.gcount());}// 处理最后一次读取的数据if (inFile.eof()) {// 写入剩余的有效数据outFile.write(buffer, inFile.gcount());}else {// 非EOF错误处理cerr << "文件读取过程中发生错误" << endl;inFile.close();outFile.close();return 1;}// 关闭文件流inFile.close();outFile.close();cout << "拷贝完成!" << endl; return 0;
}