本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
后一修订版 | 前一修订版 | ||
cs:programming:cpp:courses:cpp_basic_deep:chpt_7 [2024/11/11 06:52] – 创建 - 外部编辑 127.0.0.1 | cs:programming:cpp:courses:cpp_basic_deep:chpt_7 [2024/12/10 15:16] (当前版本) – [基于绑定的同步] codinghare | ||
---|---|---|---|
行 1: | 行 1: | ||
+ | ======深入IO====== | ||
+ | //第 7 章笔记// | ||
+ | ---- | ||
+ | ====IOStream 概述==== | ||
+ | * IOstream 把输入输出视作字节流(流式IO) | ||
+ | * 可以在其基础上构建记录 IO | ||
+ | <WRAP center round box 100%> | ||
+ | * 流式 IO | ||
+ | * 记录 IO | ||
+ | </ | ||
+ | ===需要处理的问题=== | ||
+ | * 表示形式的变化:使用格式化 / 解析将数据内部的表示(二级制)转化为字符序列(或反向转换,比如 '' | ||
+ | * 与外部设备的通信:对于不同的外部设备('' | ||
+ | <code cpp> | ||
+ | int x = 100; | ||
+ | // 通过格式化的方式,将二进制的形式转换成了相应的字符序列的表示 | ||
+ | std::cout << x << std::endl; | ||
+ | </ | ||
+ | <code cpp> | ||
+ | // 使用一块内存 | ||
+ | // 用于保存 x 或者 y | ||
+ | union | ||
+ | { | ||
+ | int x; | ||
+ | float y; | ||
+ | }; | ||
+ | x = 100; | ||
+ | // 使用不同的方式去解析同一内存中的内容 | ||
+ | // float 与 int 的解析方式不同 | ||
+ | std::cout << x << std::endl; | ||
+ | |||
+ | std::cout << y << std::endl; | ||
+ | </ | ||
+ | ===涉及的操作=== | ||
+ | * 格式化 / 解析:将内容转化为字节序列 / 将字节序列转化为内容 | ||
+ | * 缓存:将要输出的内容置于缓存中,在缓存将满的时候一次性输出 | ||
+ | * 编码转换:比如 UTF8 到 GB(转换字符所占内存的表示) | ||
+ | * 传输:从缓存中逐一进行读取 | ||
+ | ==采用的技术== | ||
+ | * '' | ||
+ | * 封装设备特性:通过不断的继承,在每一步的继承中实现不同设备的输出 | ||
+ | * 封装字符特性:使用类模板中的参数,通过替换该参数实现不同的流(比如使用 '' | ||
+ | * 实际上是类模板实例化的结果 | ||
+ | ====输入和输出==== | ||
+ | * 输入输出分为格式化和非格式化两类 | ||
+ | ===非格式化 I/O=== | ||
+ | * 输入:'' | ||
+ | * 输出: '' | ||
+ | * 特点:对计算机(性能)友好,不会通过格式化来改变内容的长度(比如输出 '' | ||
+ | <code cpp> | ||
+ | int x; | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | std:: | ||
+ | </ | ||
+ | ===格式化 I/O=== | ||
+ | * 使用移位操作符 ''>>'' | ||
+ | * 特点:对人友好,根据不同的类型(内建,自定义)进行**重载**,输出不同的格式: | ||
+ | <code cpp> | ||
+ | char c = ' | ||
+ | // 针对 char 输出字符 0 | ||
+ | std::cout << c << std::endl; | ||
+ | |||
+ | // 针对 Int 输出 ascii 48 | ||
+ | int ci = static_cast< | ||
+ | std::cout << ci << std::endl; | ||
+ | </ | ||
+ | ==格式控制== | ||
+ | * 位掩码类型:'' | ||
+ | <code cpp> | ||
+ | char c = ' | ||
+ | // 显示正负,输出 +48 | ||
+ | // 只对数值产生影响 | ||
+ | std:: | ||
+ | </ | ||
+ | * 取值随意的格式化参数:'' | ||
+ | <code cpp> | ||
+ | // 让输出占10个字符,默认往左边加空格 | ||
+ | // 默认只生效一次,读取后会被 reset 为 0 | ||
+ | std:: | ||
+ | </ | ||
+ | * 填充字符 '' | ||
+ | <code cpp> | ||
+ | // 使用 * 占位 | ||
+ | std:: | ||
+ | </ | ||
+ | ===操纵符=== | ||
+ | * manipulator:允许直接在输入输出流中使用格式控制: | ||
+ | <code cpp> | ||
+ | // 等同与之前的 showpos 用法,输出 +48 | ||
+ | // witdh 的替换,需要使用 iomanip 头文件,使用 setw() | ||
+ | // fill 的替换,使用 setfill() | ||
+ | std::cout << std:: | ||
+ | </ | ||
+ | ==其他操纵符== | ||
+ | * '' | ||
+ | ===输入相关=== | ||
+ | * 具有提取操作: | ||
+ | * 会放松对提取的类型限制:比如使用 '' | ||
+ | * 提取操作有限制(某些信息不能智能进行提取) | ||
+ | ==C风格字符串的内存越界== | ||
+ | <code cpp> | ||
+ | char y[5]; | ||
+ | // 输入长度超过 4字节 (4+\0) 均会导致内存越界 | ||
+ | // std::string 存在缓存机制,不受限制 | ||
+ | std::cin >> y; | ||
+ | |||
+ | // 使用 setw() 控制输入的长度,提取前 4 个字符 | ||
+ | std::cin >> std:: | ||
+ | </ | ||
+ | ====文件与内存操作==== | ||
+ | ===文件流=== | ||
+ | * '' | ||
+ | * 输入 / 输出 / 同时打开输入输出 | ||
+ | ==简单示例== | ||
+ | <code cpp> | ||
+ | // 传入参数为文件名 | ||
+ | // 输出流 | ||
+ | std:: | ||
+ | // 输出到文件 test_file | ||
+ | outFile << " | ||
+ | |||
+ | // 输入流 | ||
+ | std:: | ||
+ | std::string x; | ||
+ | inFile >> x; | ||
+ | std::cout << x << std::endl; | ||
+ | </ | ||
+ | ==文件流状态== | ||
+ | * 检测状态 '' | ||
+ | * 打开文件,关联流:'' | ||
+ | * 关闭文件,关闭流:'' | ||
+ | <WRAP center round box 100%> | ||
+ | 文件流会处于打开 / 关闭状态: | ||
+ | * 当与文件关联时,才会处于打开状态 | ||
+ | * 关联文件之后,无法关联另外一个文件。必须要先关闭流才能与其他文件绑定(再次打开) | ||
+ | * 缺省定义下(无文件参数),文件流默认关闭。 | ||
+ | </ | ||
+ | ==系统会使用缓存优化文件流== | ||
+ | * 每次读取写入都会非常消耗资源 | ||
+ | * 系统会将缓存内的东西积累起来,在关闭流时写入文件 | ||
+ | * 未显示关闭文件流时,'' | ||
+ | * 可以使用域控制 '' | ||
+ | <code cpp> | ||
+ | { | ||
+ | // outFile 会在大括号之后自动关闭流 | ||
+ | std:: | ||
+ | } | ||
+ | </ | ||
+ | ===打开方式=== | ||
+ | ^ 标记名 | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | ==组合使用的原理== | ||
+ | 上述的打开方式都是以二进制的形态设计的,其中每一种打开方式占一位。如果组合使用,则是以两种方式的**按位或**运算进行: | ||
+ | <code cpp> | ||
+ | // in & ate | ||
+ | // 按位 或进行组合 | ||
+ | std:: | ||
+ | std:: | ||
+ | // 得到 0011,在文件末尾进行读写 | ||
+ | </ | ||
+ | ==ate 的使用== | ||
+ | * 用于控制文件读取 / 写入的起始位置 | ||
+ | * '' | ||
+ | <WRAP center round box 100%> | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | ==trunc 的使用== | ||
+ | * 写入,并删除之前文件内的内容 | ||
+ | <code cpp> | ||
+ | // 将文件中的 hello 替代为 word | ||
+ | std:: | ||
+ | outFile << " | ||
+ | </ | ||
+ | ==binary== | ||
+ | * 可以禁止系统内定的转换 | ||
+ | ==常用组合== | ||
+ | ^打开方式^效果^加结尾标记^加二进制标记^ | ||
+ | |'' | ||
+ | |'' | ||
+ | |'' | ||
+ | |'' | ||
+ | |'' | ||
+ | |||
+ | <WRAP center round box 100%> | ||
+ | '' | ||
+ | </ | ||
+ | ===内存流=== | ||
+ | 都定义于 ''< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * 处理类型单位默认为 '' | ||
+ | ==基础用法示例== | ||
+ | * 注意格式化输出会将输入转化为字符串: | ||
+ | <code cpp> | ||
+ | int main(int argc, char const *argv[]) | ||
+ | { | ||
+ | std:: | ||
+ | // 写入内容到流 (整数) | ||
+ | // 转换整数到字符串(格式化) | ||
+ | myObj << 1234; | ||
+ | // 获取 myObj 所对应内存,使用配套的 str() 返回内容(字符串) | ||
+ | std::string ret = myObj.str(); | ||
+ | |||
+ | std::cout << ret << std::endl; | ||
+ | // | ||
+ | std:: | ||
+ | int x; | ||
+ | //将 int 赋值给 x 并打印 | ||
+ | myObj2 >> x; | ||
+ | std::cout << x << std::endl; | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | ==配合模式使用== | ||
+ | <code cpp> | ||
+ | // 默认的模式是从起始位置开始替换 | ||
+ | |||
+ | // 默认输出流中的内容是 test | ||
+ | // 所有输出都会基于 test 输出 | ||
+ | std:: | ||
+ | myObj3 << ' | ||
+ | |||
+ | // 打印结果为 test1 | ||
+ | // stringstream 会自动管理内存 | ||
+ | std::cout << myObj3.str(); | ||
+ | </ | ||
+ | ==str() 的使用注意== | ||
+ | * 返回 '' | ||
+ | * 不要**间接**使用 '' | ||
+ | * '' | ||
+ | * '' | ||
+ | ==使用内存流进行拼接优化== | ||
+ | <code cpp> | ||
+ | // 类似 vector 的空间申请 | ||
+ | // 可能每次都会进行 allocate | ||
+ | std::string s; | ||
+ | s += " | ||
+ | s += " | ||
+ | s += " | ||
+ | |||
+ | // 改良 | ||
+ | // 利用 ostringstream 的大缓存进行拼接 | ||
+ | // 不会频繁的 allocate | ||
+ | std:: | ||
+ | word << " | ||
+ | word << " | ||
+ | word << " | ||
+ | |||
+ | // 结果相同 | ||
+ | std::cout << s; | ||
+ | std::cout << word.str(); | ||
+ | </ | ||
+ | ====流的状态==== | ||
+ | * 提供额外的信息,使使用者针对状态进行对应的操作 | ||
+ | * 使用 '' | ||
+ | * 非正常的状态的值都不是 '' | ||
+ | ===三种异常状态=== | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | ==检测异常状态的方法== | ||
+ | [[https:// | ||
+ | <code cpp> | ||
+ | // 使用 cin 的成员检测 | ||
+ | std::cout << std:: | ||
+ | << std:: | ||
+ | << std:: | ||
+ | << std:: | ||
+ | // 转换为 bool 值检测 | ||
+ | << | ||
+ | </ | ||
+ | * '' | ||
+ | * 转换为 bool 值时不会考虑 '' | ||
+ | ==利用状态== | ||
+ | <code cpp> | ||
+ | // 判断之前的输入提取是否成功 | ||
+ | if(std::cin >> x) { //} | ||
+ | </ | ||
+ | ==复位流的状态== | ||
+ | * '' | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | std:: | ||
+ | </ | ||
+ | ==捕获流的异常== | ||
+ | * 可以通过 '' | ||
+ | ====流的定位==== | ||
+ | 提取写入时,需要考虑提取写入的位置(定位)。C++ 为此引入了流的定位: | ||
+ | * 获取流 | ||
+ | * 设置流 | ||
+ | ===获取流位置=== | ||
+ | * '' | ||
+ | * '' | ||
+ | * 两者返回一个为整数的 '' | ||
+ | * '' | ||
+ | ===设置流位置=== | ||
+ | * '' | ||
+ | ==两个重载版本== | ||
+ | * 设置绝对位置:接收 '' | ||
+ | * 设置相对位置:接收基本位置('' | ||
+ | ====流的同步==== | ||
+ | 系统默认的 “缓冲区满再输入到设备的” 行为很可能带来一些问题: | ||
+ | <code cpp> | ||
+ | // 如果 some test 所在流没有满,导致没有被输出到终端 | ||
+ | std::cout << "some test"; | ||
+ | std::string name; | ||
+ | // 导致输入时没有提示 | ||
+ | std::cin >> name; | ||
+ | </ | ||
+ | 这种情况下需要**刷新缓冲区**,也就是强制输出缓冲区内容送到设备上。 | ||
+ | ===基于方法的同步=== | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | // 成员调用 | ||
+ | std:: | ||
+ | // 操纵符 | ||
+ | std::cout << x << std::flush; | ||
+ | </ | ||
+ | * '' | ||
+ | * 同文件同时关联了输入输出流,要对输入流刷新,得到输出流的信息 | ||
+ | * 具体行为与编译器实现有关系 | ||
+ | * '' | ||
+ | * 行为:大于一个字符的流必须被清除出缓存区 | ||
+ | * 结果:自动立即刷新(影响性能) | ||
+ | * 通常与 '' | ||
+ | ===基于绑定的同步=== | ||
+ | C++ 中,任意流都可以绑定到一个**输出流**上。当绑定时,绑定流会记录被绑定流中的信息。当绑定时: | ||
+ | * 绑定流在每次输入(输出)时,都会刷新**被绑定流**的缓冲区 | ||
+ | * 绑定流可以同时与多个输出流绑定 | ||
+ | 这种方法实际上从另外的角度解决了之前提到过的问题: | ||
+ | <code cpp> | ||
+ | // 如果 some test 所在流没有满,导致没有被输出到终端 | ||
+ | std::cout << "some test"; | ||
+ | std::string name; | ||
+ | // 导致输入时没有提示 | ||
+ | std::cin >> name; | ||
+ | </ | ||
+ | 此时如果 '' | ||
+ | 文本输入终端。 | ||
+ | ===与 C 标准 I/O 的同步=== | ||
+ | * 缺省情况下会与 C 同步 | ||
+ | * 可以通过 '' |