深蓝学院C++基础与深度解析笔记 第 7 章 深入 IO

1. IOStream 概述(I:输入,O:输出)

●IOStream 采用流式 I/O 而非记录 I/O (类似于数据库),但可以在此基础上引入结构信息
● 所处理的两个主要问题:

– 表示形式的变化:使用格式化 / 解析在数据的内部表示与字符序列间转换
– 与外部设备的通信:针对不同的外部设备(终端、文件、内存)引入不同的处理逻辑

● 所涉及到的操作

– 格式化 / 解析
– 缓存
– 编码转换
– 传输

● 采用模板来封装字符特性,采用继承来封装设备特性

– 常用的类型实际上是类模板实例化的结果

eg: ifstream,使用模板逐步细化
在这里插入图片描述

2. 输入与输出

输入与输出分为格式化与非格式化两类
**A、非格式化 I/O :**不涉及数据表示形式的变化,非格式化对人类阅读很不友好

– 常用输入函数: get(一个) / read (多个)/ getline(一行) / gcount(缓存里剩余的)
– 常用输入函数: put(一个) / write(多个)

B、 格式化 I/O : 使用移位操作符来进行的输入 (>>) 与输出 (<<)

– C++通过操作符重载以支持内建数据类型的格式化 I/O
– 可以通过重载操作符以支持自定义类型的格式化 I/O

● 格式控制

setf()
– 可接收位掩码类型( showpos 修改某一位 )、字符类型( fill() 填充参数字符)与取值相对随意( width() ,可以接收整数,加参数个空格)的格式化参数
– 注意 width() 方法的特殊性:触发后被重置  

补充:关于showpos
在C++中,showpos是一个用于控制输出流的格式标志,它在输出正数时显示加号(+),而在输出负数时显示负号(-)。它主要用于调整输出的格式,使得输出的数值更易读。

要使用showpos标志,你需要包含<iostream>头文件,并使用std::showpos函数或使用std::ios_base::showpos成员函数来设置输出流的格式标志。

下面是一个使用showpos的简单示例:

#include <iostream>

int main() {
    int number = 10;
    double value = -3.14;

    std::cout << std::showpos;         // 设置输出流的格式标志为showpos(+-)
    std::cout << number << std::endl;  // 输出:+10
    std::cout << value << std::endl;   // 输出:-3.14

    return 0;
}

在上面的示例中,通过设置std::coutshowpos标志,输出的整数number在前面显示了一个加号,而输出的浮点数value在前面显示了一个负号。

需要注意的是,showpos标志仅影响整数和浮点数类型的输出,对于其他类型(如字符串、字符等)则不起作用。另外,showpos标志是一个永久性的标志,即一旦设置后,后续的输出将一直保持该格式,除非通过其他方式取消设置。

如果你想取消showpos标志的设置,可以使用std::noshowpos函数或std::ios_base::noshowpos成员函数来清除输出流的格式标志。例如:

std::cout << std::noshowpos;  // 取消showpos标志的设置

这样,后续的输出将不再显示加号或负号。
● 操纵符

– 简化格式化参数的设置
– 触发实际的插入与提取操作

setw()函数是定义在头文件中的一个函数,用于设置输出流中字段的宽度。
注意:setw()函数触发后被重置 ,只影响其后的输出项,即只对紧随其后的输出产生作用。如果想要持久设置输出流的字段宽度,可以使用std::setfill()函数。

另外,需要包含头文件才能使用setw()函数。

● 提取会放松对格式的限制,例如输入 ‘+a’只输出‘+’;
● 提取 C 风格字符串时要小心内存越界:
在这里插入图片描述
避免方法:可以使用setw()设置边界:
在这里插入图片描述

3. 文件与内存操作

A、 文件操作

  • basic_fstream: basic_ifstream (读取)/ basic_ofstream(写入)
    ifstream和ofstream是C++标准库中用于文件输入和输出的两个类。它们都是从基类fstream派生而来。
  • 文件流可以处于打开 / 关闭两种状态,处于打开状态时无法再次打开,只有打开时才能 I/O。
  • 当使用std::ofstream outFile(“my_file”); 这种方式时,不必进行打开关闭动作,程序会自动打开关闭的。或者使用一个域。

一般都是先写入缓存,再写到终端/文件中去

B、 文件流的打开模式

– 每种文件流都有缺省的打开方式
– 注意 ate 与 app 的异同
– binary 能禁止系统特定的转换
– 避免意义不明确的流使用方式(如 ifstream + out )

缺省的打开例子:在这里插入图片描述

C、六种文件流的打开模式:在这里插入图片描述
in | out 读写文件时,打开文件一般位于文件开头;
ate:起始位置位于文件末尾,这个位置可以移动
app: 起始位置位于文件末尾,这个位置不可以移动

D、合理的文件打开组合:
在这里插入图片描述
E、内存流相关
● 内存流: basic_istringstream / basic_ostringstream / basic_stringstream
● 也会受打开模式: in / out / ate / app 的影响
● 使用 str() 方法获取底层所对应的字符串
– cpp风格的字符串.c_str()是合理的
– 小心使用 str().c_str() 的形式获取 C 风格字符串,以下为指向一块已经释放的内存,即未定义:
在这里插入图片描述
● 基于字符串流的字符串拼接优化操作
在这里插入图片描述

4. 流的状态、定位与同步

A、流的状态
iostate
– failbit / badbit / eofbit / goodbit:在这里插入图片描述

● 检测流的状态
good( ) / fail() / bad() / eof() 方法
– 转换为 bool 值(参考cppreference)
在这里插入图片描述
从级别来看:fail() < bad()

● 注意区分 fail 与 eof
– 可能会被同时设置,但二者含意不同
– 转换为 bool 值时不会考虑 eof
eof 表示到了尾部的位置
● 通常来说,只要流处于某种错误状态时,插入 / 提取操作就不会生效
● 复位流状态

– clear :设置流的状态为具体的值(缺省为 goodbit )
– setstate :将某个状态附加到现有的流状态上

● 捕获流异常:exceptions方法
在这里插入图片描述

B、流的定位
● 获取流位置
tellg() / tellp() 可以用于获取输入 / 输出流位置 (pos_type 类型 )
– 两个方法可能会失败,成功时为当前可以输入的位置指示器,若流失败、异常时候会出现失败则为 pos_type(-1)

#include <iostream>
#include <sstream>
int main()
{
    std::ostringstream s;
    std::cout << s.tellp() << '\n';
    s << 'h';
    std::cout << s.tellp() << '\n';
    s << "ello, world ";
    std::cout << s.tellp() << '\n';
    s << 3.14 << '\n';
    std::cout << s.tellp() << '\n' << s.str();
}

输出:

0
1
13
18
hello, world 3.14

● 设置流位置
seekg() / seekp() 用于设置输入 / 输出流的位置
– 这两个方法分别有两个重载版本:
● 设置绝对位置:传入 pos_type 进行设置
● 设置相对位置:
通过偏移量(字符个数 ios_base::beg ) + 流位置符号的方式设置

– ios_base::beg 
– ios_base::cur
– ios_base::end

seekg():设置当前关联 streambuf 对象的输入位置指示器。

#include <iostream>
#include <string>
#include <sstream>
 
int main()
{
    std::string str = "Hello, world";
    std::istringstream in(str);
    std::string word1, word2;
 
    in >> word1;
    in.seekg(0); // 回溯
    in >> word2;
 
    std::cout << "word1 = " << word1 << '\n'
              << "word2 = " << word2 << '\n';
}

C、流的同步
● 基于 flush() / sync() / unitbuf 的同步

flush() 用于输出流同步,刷新缓冲区
– sync()  用于输入流同步,其实现逻辑是编译器所定义的
– 输出流可以通过设置 unitbuf 来保证每次输出后自动同步

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
unitbuf 会立即刷新,但是会影响性能

补充:std::cout 和std::cerro有什么区别?
std::coutstd::cerr 是 C++ 标准库中用于输出的流对象,但它们在以下几个方面有所不同:

  1. 输出位置: std::cout 对应标准输出流(stdout),而 std::cerr 对应标准错误流(stderr)。这意味着通过 std::cout 输出的内容会显示在正常的输出中,而 std::cerr 输出的内容则被视为错误信息,并显示在错误流中。

  2. 缓冲机制: std::cout 使用标准输出缓冲区,而 std::cerr 使用非缓冲的错误流。这意味着 std::cout 输出的内容通常会在缓冲区满、换行符出现或程序正常终止时刷新并显示,而 std::cerr 的输出则会立即显示,无需等待缓冲区刷新。

  3. 用途: std::cout 主要用于一般的标准输出,例如打印程序的正常输出、结果等。而 std::cerr 则常用于报告错误、警告和异常情况,以便能够及时注意和处理程序中的问题。

由于 std::cerr 是一个非缓冲的流,并且将其输出视为错误信息,因此在编写调试代码和处理异常时,使用 std::cerr 可以获得更及时和准确的错误报告,以便于调试和追踪问题。而 std::cout 则适合用于一般的输出和日志记录。

需要注意的是,std::coutstd::cerr 都是流对象,可以像其他流一样使用流操作符(<<)来输出数据。例如:

#include <iostream>

int main() {
    int value = 42;
    std::cout << "Value: " << value << std::endl; // 正常输出
    std::cerr << "Error: Something went wrong!" << std::endl; // 错误输出
    return 0;
}

在使用时,根据你的需求选择适当的输出流对象。-end

● 基于绑定 (tie) 的同步

– 流可以绑定到一个输出流上,这样在每次输入 / 输出前可以刷新输出流的缓冲区
– 比如: cin 绑定到了 cout 上,所以输入之前的输出 都会打印出来

● 与 C 语言标准 IO 库的同步

– 缺省情况下, C++ 的输入输出操作会与 C 的输入输出函数同步

可以通过 sync_with_stdio 关闭该同步

本章大量参考了:cppreference 中流相关的内容。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
长整数相加是一种基本的数学运算,是我们在日常生活中经常会遇到的问题。当然,对于大数的相加,我们需要使用一些特殊的方法来进行处理。 首先,我们需要将长整数分解为各个位数上的数字,并将其以数组或链表的形式存储起来。然后,从低位开始,一次将对应位数的数字相加,并将结果存储在一个新的数组或链表中。如果相加的结果大于等于10,则需要向高位进位。 在进行长整数相加时,我们需要考虑的是两个长整数的位数是否相等。如果不相等,我们可以在运算之前,将位数较少的长整数的高位补零,使其位数相等。这样,我们就可以按位相加了。 在处理进位时,我们需要注意的是进位可能会一直影响到最高位,因此我们还需要一个额外的变量来记录进位。同时,在最后一次相加后,如果进位不为0,我们还需要将进位添加到结果的最高位上。 由于长整数相加涉及到的数位较多,所以我们需要用到循环来处理每一位的相加和进位。在循环的过程中,我们还需要判断每一位的边界情况,例如,当位数相同时,如果数组或链表已经遍历完,我们仍需考虑进位的情况。 综上所述,长整数的相加是一种基础且常见的数学运算,但在处理大数时,需要注意相加过程中的进位和边界情况。这需要我们熟悉长整数的表示形式,并能够使用适当的算法来进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

甜橙の学习笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值