当前位置: 首页 > article >正文

C++的 I/O 流

本文把复杂的基类和派生类的作用和关系捋出来,具体的接口请参考相关文档

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 对象:

在标头 <iostream> 定义

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 对象:

在标头 <iostream> 定义

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;
}


http://www.kler.cn/a/535037.html

相关文章:

  • Java 23新特性
  • 2025蓝桥杯JAVA编程题练习Day2
  • C语言按位取反【~】详解,含原码反码补码的0基础讲解【原码反码补码严格意义上来说属于计算机组成原理的范畴,不过这也是学好编程初级阶段的必修课】
  • (一)DeepSeek大模型安装部署-Ollama安装
  • Meta推动虚拟现实:Facebook如何进入元宇宙时代
  • 用 Python 绘制爱心形状的简单教程
  • CentOS 6.5编译Rsyslog 8.1903.0
  • web3.0技术
  • 计算机组成与接口
  • 天童教育:帮助孩子建立稳定的自信心
  • 如何从0开始做自动化测试?
  • 深度学习系列--03.激活函数
  • Three.js实现炫酷图片粒子化效果:从聚合到扩散的动态演变
  • SystemVerilog系统函数之$system详细使用指南与举例
  • DeepSeek核心关键技术 (冷启动,拒绝采样,蒸馏,多头潜注意力,MoE等) 解读
  • 【Axure高保真原型】中继器表格控制动态面板
  • 不含101的数
  • 微信小程序~django Petting pets(爱抚宠物)小程序
  • 机器学习-线性回归(参数估计之结构风险最小化)
  • JavaScript系列(62)--实时通信系统实现详解
  • 使用page assist浏览器插件结合deepseek-r1 7b本地模型
  • 支持 APQP (先期产品质量策划) 的软件系统-汽车电子行业专用研发管理信息化平台
  • ‌双非硕士的抉择:自学嵌入式硬件开发还是深入Linux C/C++走软开?
  • mongodb 使用内存过大分析
  • 文档解析技术:如何高效提取PDF扫描件中的文字与表格信息?
  • 流浪地球发动机启动问题解析与实现