《C++高级编程》读书笔记(十三:C++ I/O揭秘)

news2024/11/20 1:22:47

1、参考引用

  • C++高级编程(第4版,C++17标准)马克·葛瑞格尔

2、建议先看《21天学通C++》 这本书入门,笔记链接如下

  • 21天学通C++读书笔记(文章链接汇总)

1. 使用流

1.1 流的含义

  • C++ 中预定义的流

在这里插入图片描述

  • 缓冲的流和非缓冲的流的区别在于:前者不是立即将数据发送到目的地,而是缓冲输入的数据,然后以块方式发送,而非缓冲的流则立即将数据发送到目的地
    • 缓冲的目的通常是提高性能,对于某些目的地(如文件)而言,一次性写入较大的块时速度更快
    • 注意,始终可使用 flush() 方法刷新缓冲区,强制要求缓冲的流将其当前所有的缓冲数据发送到目的地
  • 所有输入流都有一个关联的来源,所有输出流都有一个关联的目的地
  • 有关流的另一个要点是:流不仅包含普通数据,还包含称为当前位置的特殊数据。当前位置指的是流将要进行下一次读写操作的位置

1.2 流的来源和目的地

  • 流可应用于任何接收数据或生成数据的对象,C++ 中流可使用 3 个公共的来源和目的地:控制台、文件和字符串
    • 1. 控制台
      • 控制台输入流允许程序在运行时从用户那里获得输入,使程序具有交互性
      • 控制台输出流向用户提供反馈和输出结果
    • 2. 文件:文件流从文件系统中读取数据并向文件系统写入数据
      • 文件输入流适用于读取配置数据、读取保存的文件,也适用于批处理基于文件的数据等任务
      • 文件输出流适用于保存状态数据和提供输出等任务
      • 文件流包含 C 语言输出函数 fprintf()、fwrite() 和 fputs() 功能,及输入函数 fscanf()、fread() 和 fgets() 功能
    • 3. 字符串:字符串流是将流隐喻应用于字符串类型的例子
      • 使用字符串流时,可像处理其他任何流一样处理字符数据
      • 使用流式语法为优化提供了机会,而且比直接使用 string 类方便
      • 字符串流包含 sprintf()、sprintf_s() 和 sscanf() 的功能,以及很多 C 语言字符串格式化函数的功能

1.3 流式输出

1.3.1 输出的基本概念
  • 输出流定义在 <ostream> 头文件中,大部分程序都包含 <iostream> 头文件,该头文件包含输入流和输出流的头文件
  • 使用输出流的最简单方法是使用 << 运算符
    int i = 7;
    cout << i << endl;
    
    char ch = 'a';
    cout << ch << endl;
    
    string myString = "Hello World!";
    cout << myString << endl;
    
    // 输出
    7
    a
    Hello World!
    
  • cout 流是写入控制台的内建流,控制台也称为标准输出。可将 << 的使用串联起来,从而输出多个数据段。这是因为 << 运算符返回一个流的引用,因此可以立即对同一个流再次应用 << 运算符
    int j = 11;
    cout << "The value of j is " << j << "!" << endl;
    
    // 输出
    The value of j is 11!
    
  • \n 和 endl 的区别是,\n 仅开始一个新行,而 endl 还会刷新缓存区
    • 使用 endl 时要小心,因为过多的缓存区刷新会降低性能
1.3.2 输出流的方法
  • put() 和 write()
    • put() 接收单个字符,write() 接收一个字符数组
    • 传给这些方法的数据按照原本的形式输出,没有做任何特殊的格式化和处理操作
    cout.put('a');
    
    const char* test = "hello there\n";
    cout.write(test, strlen(test));
    
  • flush()
    • 向输出流写入数据时,流不一定会将数据立即写入目的地。大部分输出流都会进行缓冲,也就是积累数据,而不是立即将得到的数据写出去。缓冲的目的通常是提高性能,对于某些目的地(如文件)而言,与逐字符写入相比,一次性写入较大的块时速度更快(但是 cerr 输出流就不会缓存其输出
    • 显式要求流刷新缓存的方法是调用流的 flush() 方法
    cout << "abc";
    cout.flush();
    cout << "def";
    cout << endl;
    
1.3.3 处理输出错误
  • 输出错误可能会在多种情况下出现:试图打开一个不存在的文件;因为磁盘错误导致写入操作失败,例如磁盘已满

  • 调用流的 good() 方法可以判断这个流当前是否处于正常状态

    if (cout.good()) {
        cout << "All good" << endl;
    }
    
  • 方法 fail() 在最近一次操作失败时返回 true,但没有说明下一次操作是否也会失败。例如,对输出流调用 flush() 后,可调用 fail() 确保流仍然可用

    cout.flush();
    if (cout.fail()) {
        cerr << "Unable to flush to standard out" << endl;
    }
    
  • 流具有可转换为 bool 类型的转换运算符。转换运算符与调用 !fail() 时返回的结果相同。因此,可将前面的代码段重写为

    cout.flush();
    if (!cout) {
        cerr << "Unable to flush to standard out" << endl;
    }
    
  • 还可要求流在发生故障时抛出异常。然后编写一个 catch 处理程序来捕捉 ios_base::failure 异常,然后对这个异常调用 what() 方法,获得错误的描述信息,调用 code() 方法获得错误代码

    cout.exceptions(ios::failbit | ios::badbit | ios::eofbit);
    try {
        cout << "Hello World." << endl;
    } catch (const ios_base::failure& ex) {
        cerr << "Caught exception: " << ex.what() << ", error code = " << ex.code() << endl;
    }
    
  • 通过 clear() 方法重置流的错误状态

    cout.clear();
    

1.4 流式输入

1.4.1 输入的基本概念
  • 通过输入流,可采用两种简单方法来读取数据。第一种方法类似于 << 运算符,<< 向输出流输出数据。读入数据对应的运算符是 >>,通过 >> 从输入流读入数据时,代码提供的变量保存接收的值
    string userInput;
    cin >> userInut;
    cout << "User input was " << userInput << endl;
    
    • 默认情况下 >> 运算符根据空白字符对输入值进行标志化。例如,如果用户运行以上程序,并键入 hello there 作为输入,那么只有第一个空白字符 (在这个例子中为空格符) 之前的字符才会存储在 userInput 变量中,输出如下
    User input was hello // 在输入中包含空白字符的一种方法是使用 get()
    
  • 通过输入流可以读入多个值,并且可根据需要混合和匹配类型
    • >> 运算符会根据空白字符符号化,因此 getReservationData() 函数不允许输入带有空白字符的姓名。一种解决方法是使用 unget() 方法
    void getReservationData {
        string guestName;
        int partySize;
        cout << "Name and number of guests: ";
        cin >> guestName >> partySize; // 使用 cin 会立即刷新 cout 缓存区
        cout << "Thank you, " << guestName << "." << endl;
    }
    
1.4.2 处理输入错误
  • 输入流提供了一些方法用于检测异常情形。大部分和输入流有关的错误条件都发生在无数据可读时。例如,可能到达流尾(称为文件末尾,即使不是文件流)。查询输入流状态的最常见方法是在条件语句中访问输入流。例如,只要 cin 保持在 “良好” 状态,下面的循环就继续进行
    while (cin) {
        // ...
    }
    
    // 同时还可输入数据
    while (cin >> ch) {
        // ...
    }
    
  • 还可在输入流上调用 good()、bad() 和 fail() 方法,就像输出流那样
1.4.3 输入方法
  • get()

    • get() 方法允许从流中读入原始输入数据。get() 的最简单版本返回流中的下一个字符,其他版本一次读入多个字符。get() 常用于避免 >> 运算符的自动标志化(可包含空格
    string readName(istream& stream) {
        string name;
        while (stream) {
            int next = stream.get();
            // ...
        }
    }
    
  • unget()

    • 调用 unget() 会导致流回退一个位置,将读入的前一个字符放回流中。调用 fail() 方法可查看 unget() 是否成功。例如,如果当前位置就是流的起始位置,那么 unget() 会失败
  • putback()

    • putback() 和 unget() 一样,允许在输入流中反向移动一个字符。区别在于 putback() 方法将放回流中的字符接收为参数
    char ch1;
    cin >> ch1;
    cin.putback('e'); // 'e' 将是从流中读出的下一个字符
    
  • peek()

    • 通过 peek() 方法可预览调用 get() 后返回的下一个值
    • 非常适合于:在读取前需要预先查看一个值的场合
  • getline()

    • 从输入流中获得一行数据是一种常见需求,有一个方法能完成这个任务:getline() 方法用一行数据填充字符缓存区,数据量最多至指定大小,指定的大小中包括 \0 字符。因此,下面的代码最多从 cin 中读取 kBufferSize - 1 个字符,或者读到行尾为止
    char buffer[kBufferSize] = {0};
    cin.getline(buffer, kBufferSize);
    
    • 还有一个用于 C++ 字符串的 std::getline() 函数。该函数定义在 <string> 头文件和 std 名称空间中,接收一个流引用、一个字符串引用和一个可选的分隔符作为参数。该版本 getline() 函数的优点是不需要指定缓存区的大小
    string myString;
    std::getline(cin, myString);
    

2. 字符串流

  • 可通过字符串流将流语义用于字符串,通过这种方式,可得到一个内存中的流来表示文本数据
    • 例如,在 GUI 应用程序中,可能需要用流来构建文本数据,但不是将文本输出到控制台或文件中,而是把结果显示在 GUI 元素中,例如消息框和编辑框
    • 另一个例子是,要将一个字符串流作为参数传给不同函数,同时维护当前的读取位置,这样每个函数都可以处理流的下一部分。字符串流也非常适合于解析文本,因为流内建了标记化的功能
  • std::ostringstream 类用于将数据写入字符串,std:istringstream 类用于从字符串中读出数据。这两个类都定义在 <sstream> 头文件中。ostringstream 和istringstream 把同样的行为分别继承为 ostream 和 istream
    ostringstream outStream;
    while (cin) {
        string nextToken;
        cin >> nextToken;
        outStream << nextToken << "\t";
    }
    
    Muffin creatMuffin(istringstream& stream) {
        string description;
        int size;
        bool hasChips;
    
        // ...
    
        stream >> description >> size >> boolalpha >> hasChips;
    }
    

相对于标准 C++字符串,字符串流的主要优点是除了数据之外,这个对象还知道从哪里进行下一次读或写操作,这个位置也称为当前位置。与字符串相比,字符串流的另一个优势是支持操作算子和本地化,格式化功能更加强大

3. 文件流

  • 文件本身非常符合流的抽象,因为读写文件时,除数据外,还涉及读写的位置。在 C++ 中,std:ofstream 和 std::ifstream 类提供了文件的输入输出功能。这两个类在 <fstream> 头文件中定义
  • 输出文件流和其他输出流的唯一主要区别在于:文件流的构造函数可以接收文件名以及打开文件的模式作为参数
    • 默认模式是写文件 (ios base:out),这种模式从文件开头写文件,改写任何已有的数据
    • 给文件流构造函数的第二个参数指定常量 ios base::app,还可按追加模式打开输出文件流

在这里插入图片描述

  • 可组合模式,例如,如果要打开文件用于输出(以二进制模式),同时截断现有数据,可采用如下方式指定打开模式

    ios_base::out | ios_base::binary | ios_base::trunc
    
  • ifstream 自动包含 ios_base::in 模式,ofstream 自动包含 ios_base::out 模式,即使不显式地将 in 或 out 指定为模式,也同样如此。下面的程序打开文件 test.txt,并输出程序的参数。ifstream 和 ofstream 析构函数会自动关闭底层文件,因此不需要显式调用 close()

    int main() {
        ofstream outFile("test.txt", ios_base::trunc);
        if (!outFile.good()) {
            cerr << "..." << endl;
            return -1;
        }
        // ...
    }
    

3.1 文本模式与二进制模式

  • 默认情况下,文件流在文本模式中打开。如果指定 ios_base::binary 标志,将在二进制模式中打开文件。在二进制模式中,要求把流处理的字节写入文件。读取时,将完全按文件中的形式返回字节
  • 在文本模式中,会执行一些隐式转换,写入文件或从文件中读取的每一行都以 \n 结束

3.2 通过 seek() 和 tell() 在文件中转移

  • 所有的输入流和输出流都有 seek() 和 tell() 方法
    • seek() 方法允许在输入流或输出流中移动到任意位置
    • 可通过 tell() 方法查询流的当前位置,这个方法返回一个表示当前位置的 streampos 值。利用这个结果,可在执行 seek() 之前记住当前标记的位置,还可查询是否在某个特定位置

3.3 将流链接在一起

  • 任何输入流和输出流之间都可以建立链接,从而实现 “访问时刷新” 的行为。换句话说,当从输入流请求数据时,链接的输出流会自动刷新。这种行为可用于所有流,但对于可能互相依赖的文件流来说特别有用

  • 通过 tie() 方法完成流的链接。要将输出流链接至输入流,对输入流调用 tie() 方法,并传入输出流的地址。要解除链接,传入 nullptr

  • 下面的程序将一个文件的输入流链接至一个完全不同的文件的输出流,也可链接至同一个文件的输出流,但是双向 I/O 可能是实现同时读写同一个文件的更优雅方式

    ifstream inFile("input.txt");
    ofstream outFile("output.txt");
    
    inFile.tie(&outFile); // 建立链接
    
    outFile << "Hello there!"; // outFile 未被刷新,因为 std::endl 未发送
    
    // 会触发 flush()
    string nextToken;
    inFile >> nextToken; // outFile 被刷新
    
  • 可通过这种机制保持两个相关文件的同步:每次写入一个文件时,发送给另一个文件的缓存数据会被刷新

    • 这种流链接的一个例子是 cout 和 cin 之间的链接。每当从 cin 输入数据时,都会自动刷新 cout
    • cerr 和 cout 之间也存在链接,这意味着到 cerr 的任何输出都会导致刷新 cout,而 clog 未链接到 cout

4. 双向 I/O

  • 目前,本章把输入流和输出流当作独立但又关联的类来讨论。事实上,有一种流可同时执行输入和输出,双向流可同时以输入流和输出流的方式操作
  • 双向流是 iostream 的子类,而 iostream 是 istream 和 ostream 的子类,因此这是一个多重继承示例。显然双向流支持 >> 和 << 运算符,还支持输入流和输出流的方法
  • fstream 类提供了双向文件流。fstream 特别适用于需要替换文件中数据的应用程序,因为可通过读取文件找到正确的位置,然后立即切换为写入文件
  • 还可通过 stringstream 类双向访问字符串流

双向流用不同的指针保存读位置和写位置。在读取和写入之间切换时,需要定位到正确的位置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/699448.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot:配置Jetty容器

&#x1f468;‍&#x1f393;作者&#xff1a;bug菌 ✏️博客&#xff1a; CSDN、 掘金、 infoQ、 51CTO等 &#x1f389;简介&#xff1a;CSDN、 掘金等社区优质创作者&#xff0c;全网合计7w粉&#xff0c;对一切技术都感兴趣&#xff0c;重心偏Java方向&#xff0c;目前运营…

c语言查漏补缺

例子一 #include<iostream> using namespace std;int main() {int a[5]{1,2,3,4,5};int* ptr (int*)(a1);printf("%d",*(ptr-1));return 0; }输出结果是&#xff1a;1&#xff0c;这个很好理解&#xff0c;数组名即数组的首地址&#xff0c;&#xff08;a1&a…

Android 13(T) - binder阅读(5)- 使用ServiceManager注册服务2

上一篇笔记我们看到了binder_transaction&#xff0c;这个方法很长&#xff0c;这一篇我们将把这个方法拆分开来看binder_transaction做了什么&#xff0c;从而学习binder是如何跨进程通信的。 1 binder_transaction static void binder_transaction(struct binder_proc *proc…

如何将自定义起步依赖打成包

说明&#xff1a;之前做过一个自定义的OSS起步依赖&#xff08;http://t.csdn.cn/9aYr5&#xff09;&#xff0c;但是当时只是新建了一个Demo模块来测试自定义起步依赖能成功使用&#xff0c;本文介绍如何把自定义的起步依赖打成jar包&#xff0c;供其他项目或其他人引入依赖就…

华为云CodeArts TestPlan测试设计:守护产品开发质量之魂

华为产品质量的守护神 华为云CodeArts TestPlan测试设计是华为产品质量的守护神。华为云CodeArts TestPlan提供多维度测试设计模板、“需求-场景-测试点-测试用例” 四层测试分解设计能力&#xff0c;启发测试人员发散性思维&#xff0c;对项目环境、测试对象、质量标准、测试…

【SpringBlade-权限缺陷】API鉴权逻辑缺陷漏洞

目录 一、理论部分 简介 如何通过认证 API 鉴权 配置API放行 细颗粒度鉴权配置 结尾 二、实战部分 一、理论部分 简介 Secure 基于 JWT 封装&#xff0c;每次请求的时候&#xff0c;会拦截到需要鉴权的API请求&#xff0c;并对其请求头携带的Token进行认证。若 Token…

Js时间倒计时

&#x1f607;作者介绍&#xff1a;一个有梦想、有理想、有目标的&#xff0c;且渴望能够学有所成的追梦人。 &#x1f386;学习格言&#xff1a;不读书的人,思想就会停止。——狄德罗 ⛪️个人主页&#xff1a;进入博主主页 &#x1f5fc;推荐系列&#xff1a;点击进入 &#…

vue 0-1搭建项目

vue 从0-1搭建项目 前提&#xff1a;进入到需要创建项目的文件夹中&#xff0c;打开命令行窗口 windowsr 打开命令行窗口 创建vue2.0项目&#xff1a; 自动创建 1.vue create 项目名称 仅包含3个功能&#xff1a;vue2/3,babel&#xff0c;eslint 手动创建 1.vue create 项目名…

ModaHub魔搭社区:详解向量数据库Milvus的Mishards:集群分片中间件(五)

目录 在 Kubernetes 中部署 Mishards 集群 安装前提 安装流程 卸载 Mishards 从单机升级到 Mishards 集群 注意事项 基本案例 在 Kubernetes 中部署 Mishards 集群 安装前提 Kubernetes 版本 1.10 及以上Helm 版本 2.12.0 及以上 关于 Helm 的使用请参考 Helm 使用指…

【python入门系列】第一章:Python基础语法和数据类型

文章目录 前言一、简单语法1. 注释 这是一个单行注释2. 变量 二、数据类型1.字符串2.整数3.浮点数4.布尔值5.列表 三、运算符1.算术运算符&#xff1a;用于执行基本的算术操作&#xff0c;如加、减、乘和除。2.比较运算符&#xff1a;用于比较两个值的大小或相等性。3.逻辑运算…

01.网络编程-基础概念

网络编程就是指编写互联网项目&#xff0c;项目可以通过网络传输数据进行通讯 网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包&#xff0c;在接收端按照规定好的协议把包进行解析&#xff0c;从而提取出对应的信息&#xff0c;达到通信的目的 1.1 软件结构…

Oracle 11g安装配置完美教程 - Windows

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

用Java解决华为OD机试考题,目标300+真题,清单奉上,祝你上岸

华为OD机考大纲 其它语言版本华为 OD 机试题清单&#xff08;机试题库还在逐日更新&#xff09;详细大纲 其它语言版本 本目录为华为od机试JS题解目录&#xff0c;其它版本清单如下 ⭐️华为OD机考 Python https://blog.csdn.net/hihell/category_12199275.html ⭐️华为OD机考…

BOSHIDA DC电源模块低温试验检测详细分析

BOSHIDA DC电源模块低温试验检测详细分析 DC电源模块的低温试验是电源应用领域中的一项重要测试&#xff0c;它可以检测模块在低温环境下的性能表现是否与设计要求相符。这是因为在一些极端环境下&#xff0c;电源模块的性能会受到影响&#xff0c;从而影响整个系统的运行稳定…

山西电力市场日前价格预测【2023-06-30】

日前价格预测 预测明日&#xff08;2023-06-30&#xff09;山西电力市场全天平均日前电价为362.38元/MWh。其中&#xff0c;最高日前价格为477.68元/MWh&#xff0c;预计出现在21: 15。最低日前电价为247.28元/MWh&#xff0c;预计出现在13: 00。以上预测仅供学习参考&#xff…

Pytorch深度强化学习(3):详解K摇臂赌博机模型和ϵ-贪心算法

目录 1 K-摇臂赌博机2 ϵ \epsilon ϵ-贪心算法3 softmax算法4 Python实现与分析 1 K-摇臂赌博机 单步强化学习是最简单的强化学习模型&#xff0c;其以贪心策略为核心最大化单步奖赏 如图所示&#xff0c;单步强化学习的理论模型是 K K K-摇臂赌博机( K K K-armed bandit)&…

内部类之成员内部类

我们在阅读源码的过程中&#xff0c;会遇到很多内部类&#xff0c;让我们阅读难度增加。这篇文章主要介绍成员内部类的作用 1.成员内部类可以访问外部类的所有方法和成员变量&#xff08;不论是静态的还是非静态的&#xff09; package com.high.concurrency.inner;/*** auth…

如何让chatgpt给我们实现一个桌面便签?

我&#xff1a; 用Java开发一款桌面便签软件。功能需求&#xff1a;1、便签可以钉在桌面任意角落&#xff1b;2、便签内容实时保存到本地&#xff1b;3、有新增按钮&#xff0c;点击新增时清除当前面板上的内容&#xff0c;新建一条新的便签内容&#xff0c;并保存到本地&#…

通往AGI之路:揭秘英伟达A100、A800、H800、V100在高性能计算与大模型训练中的霸主地位

AGI | NLP | A100 | H100 | Nvidia | Aurora GPT| LLM | A800 | V100 | Intel | ChatGPT 日前&#xff0c;随着深度学习、高性能计算、大模型训练等技术的保驾护航&#xff0c;通用人工智能时代即将到来。各个厂商也都在紧锣密鼓的布局&#xff0c;如英伟达前段时间发布GH 200…

C3.ai:一个即将“破灭”的人工智能泡沫

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 C3.ai仅适用于能承受高风险波动的投资者 猛兽财经曾在之前分析过C3.ai&#xff08;AI&#xff09;这家公司&#xff0c;认为C3.ai可能在借助人工智能热潮炒作自己。截止撰写本文时&#xff0c;C3.ai的股价已跌到了到2…