【c++ Prime 学习笔记】第8章 IO库
- C++语言不直接处理输入输出,而是通过标准库中的一组类来处理IO
- 1.2节介绍的IO库:
istream
(输入流)类型,提供输入ostream
(输出流)类型,提供输出cin
,是istream对象,从标准输入读取数据cout
,是ostream对象,向标准输出写数据cerr
,是ostream对象,用于输出错误信息,写到标准错误>>
运算符,从istream对象读输入<<
运算符,向ostream对象写输出getline
函数,从给定的istream读取一行数据,存入string对象
8.1 IO 类
- 实际程序不仅要从
控制台窗口
进行IO操作,还需要读写文件
,而且用IO操作处理字符串
也很方便。另外,程序可能需要读写宽字符
文本 - 标准库的IO类型在3个头文件中:
iostream
头文件定义了读写流的基本类型fstream
头文件定义了读写命名文件的类型sstream
头文件定义了读写string对象的类型
- 为支持宽字符语言,标准库定义的IO类也可操纵wchar_t类型数据,它对应的类型和函数名以w开始
IO 类型间的关系
- 设备类型和字符宽度不会影响IO操作,例如>>运算符对控制台窗口、文件、字符串都可用,对char和wchar_t也可用
- 标准库通过
继承机制
实现不同类型的流之间的差异可以忽略,利用模板- 声明一个类
继承
自另一个类,则通常可将派生类
当作基类
来使用 - 类型ifstream和istringstream都继承自istream,即可以像使用istream对象一样使用ifstream和istringstream对象
- 声明一个类
- 本节所述的流特性都可无差别地应用于普通流、文件流、字符串流,以及char和wchar_t版本
8.1.1 IO 对象无拷贝或赋值
- 不能拷贝IO对象,不能给IO对象赋值,不能将形参或返回类型设为流类型
- 进行IO操作的函数通常以
引用
方式传递和返回流 - 读写IO会改变其状态,故传递和返回的引用不能const
8.1.2 条件状态
- IO很可能发生错误。一些错误可恢复,另一些错误在系统深处,超过了程序可处理的范围
- 流发生错误的例子:从外部读到的类型和程序中需要的类型不匹配时,流进入错误状态
- 一个流一旦发生错误,其上后续的IO操作都会失败。只有无错误才能继续读写
- 使用流时应检查状态,将其当作条件。如
while(cin>>word)
,其中>>
返回流的状态,操作成功则流有效
查询流的状态
- 当流作为条件时,只能知道是否有效,不知道发生了什么
- IO库定义了一个机器无关的
iostate
类型,它提供了表达流状态的完整功能 - iostate定义了4个iostate类型的constexpr值来表示特定的位模式。这些值用于表示特定的条件状态,可与位运算符一起使用来一次检测或设置多个标志位
badbit
表示系统级错误,不可恢复。一旦它被置位,流就无法使用failbit
在发生可恢复错误时被置位,如读取类型错误- 读到EOF处,
eofbit
和failbit都被置位 goodbit
的值为0表示无错误
- badbit、failbit、eofbit中的任一个被置位,则检测流状态的条件都会失败
- 标准库定义了一组函数来查询这些iostate标志位的状态,
good()
在所有错误位均未置位时返回truebad()
、fail()
、eof()
均在对应错误位被置位时返回true- badbit被置位时,
fail()
也会返回true - 将流当作条件时,等价于判断
!fail()
,而eof()
和bad()
操作只能表示特定错误
管理条件状态
- 流对象
rdstate()
操作返回iostate类型值,表示当前状态 setstate()
操作接受iostate类型值,将给定的条件位 置位,表示发生了对应错误clear
成员函数有两个版本:clear()
清除所有错误标志位clear(flags)
接受iostate类型值,表示流的新状态
auto old_state=cin.rdstate(); //保存cin的状态
cin.clear(); //清除错误位,使之有效
process_input(cin); //使用cin
cin.setstate(old_state); //将cin置为原来的状态
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit); //复位failbit和badbit,其他位不变
8.1.3 管理输出缓冲
- 每个输出流都管理一个缓冲区,用于保存程序读写的数据
- 由于写设备很耗时,操作系统将程序的多个输出操作组合成单一的设备写操作,可大幅提高性能
- 导致
缓冲刷新
(即数据真正写到设备/文件)的原因:- 程序
正常结束
,缓冲刷新作为main的return的一部分 缓冲区满
时刷新- 用操纵符如
endl
、flush
、ends
显式刷新 - 输出操作之后可用操纵符
unitbuf
设置流的内部状态,来清空缓存区。默认时cerr时unitbuf的,即cerr的内容立即刷新 - 一个输出流可能被
关联
到另一个流。读写被关联的流时,关联到的流的缓冲被刷新。例如,cin和cerr关联到cout,故读cin或写cerr都将使cout刷新
- 程序
刷新输出缓冲区
- 操纵符endl、flush、ends显式刷新:
endl
输出换行并刷新flush
直接刷新,不输出字符ends
输出空字符并刷新
cout<<"hi!"<<endl; //输出"hi!"和换行符,刷新缓冲
cout<<"hi!"<<flush; //输出"hi!",刷新缓冲
cout<<"hi!"<<ends; //输出"hi!"和空字符,刷新缓冲
unitbuf 操纵符
unitbuf
操作符告诉流,接下来的每次写操作之后都进行flushnounitbuf
操作符重置流,使其恢复默认刷新
cout<<unitbuf; //之后的所有cout输出都将立即刷新缓冲
cout<<nounitbuf; //恢复cout的默认刷新
关联输入和输出流
- 若程序异常终止,输出缓冲不会被刷新。因此调试崩溃的程序时要保证输出数据确实被刷新
- 当一个输入流被关联到一个输出流时,任何从该输入流读取的操作都刷新关联的输出流
- 交互式系统通常应关联输入输出流,保证所有提示信息都在读操作前被打印
tie
函数有两个重载的版本tie()
返回指向输出流的指针,若未关联到流则返回空指针tie(ostream)
接受一个指向ostream的指针,将自己关联到此ostream。用法如x.tie(&o)
将流x
关联到输出流o
- 每个流最多同时关联一个流,但多个流可同时关联到同一个ostream
cin.tie(&cout); //将cin关联到cout
ostream *old_tie=cin.tie(nullptr); //使cin不再关联到任何流
cin.tie(&cerr); //将cin关联到cerr
cin.tie(&old_tie); //重建cin和cout间的正常关联
8.2 文件输入输出
- 头文件
fstream
定义了3个IO类来读写文件:- 类
ifstream
从给定文件读数据 - 类
ofstream
向给定文件写数据 - 类
fstream
可读写文件
- 类
- fstream中的这些类型继承自iostream的对应类型,它们提供的操作类似cin和cout,即
<<
、>>
、getline
等,以及8.1节中的所有操作 - 表8.3是fstream中比iostream新增的成员
8.2.1 使用文件流对
- 要读写文件时应定义
文件流对象
,并将对象与文件关联
成员函数 open 和 close
open(s)
将对象与文件关联,定位给定文件并视情况打开为读/写模式- 对一个已经打开的文件流调用open会失败,并会导致failbit被置位
ifstream in(ifile); //初始化时关联到文件
ofstream out; //默认初始化
out.open(ifile+".copy"); //打开文件
if (out){/*使用out*/} //先检查是否成功关联到文件,再使用流
in.close(); //一个文件流打开另一个文件前,必须关闭当前文件
in.open(ifile+"2"); //打开另一个文件
自动构造和析构
- 创建文件流对象时可在构造函数中提供文件名
filename
,此时open(filename)
会被自动调用。在C++11之前,文件名只能是C风格字符串,C++11后文件名可是string对象或C风格字符串 - 当一个fstream对象离开其作用域时,与之关联的文件会自动关闭
for ( auto p = argv + 1; p != argv +argc; ++p){
ifstream input(*p);//每个循环 input都要被创建和销毁
if(input){
process(input);
} else
cerr << "couldn't open: " + string(*p);
}
- 在要求
基类
对象的地方,可用派生类
对象代替。例如,接受iostream引用/指针的函数,可用对应的fstream/sstream引用/指针来调用 - 可以先定义空文件流对象(默认初始化),再调用
open
与文件关联 - 如果调用
open
失败,则failbit
被置位 - 对已经关联到文件的流再次调用
open
会失败,并将failbit
置位 - 如要将已经关联到文件的流关联到另一个文件,必须先用
close
关闭已关联的文件
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
int main() {
ifstream in("./test.txt");
if (!in) {
cerr << "无法打开文件" << endl;
}
string line;
vector<string> words;
while (getline(in,line))
words.push_back(line);
in.close();
for (auto customLine : words)
cout << customLine << endl;
//另外一种写法 迭代器
vector<string>::const_iterator it = words.begin();
while (it!=words.end())
{
cout << *it++ << endl;
}
system("pause");
return 0;
}
8.2.2 文件模式
每个流都有一个关联的文件模式
- 用open打开文件或用文件名构造文件流对象时,都可指定文件模式
- 指定文件模式的限制:
- 只可对ofstream或fstream对象设定
out
模式 - 只可对ifstream或fstream对象设定
in
模式 - 只有当out也被设定时才可设定
trunc
模式 - 只要trunc未被设定,就可设定app模式。在
app
模式下,文件总以out模式被打开 - 默认情况下,即使未指定trunc,以out模式打开的文件也会被截断,即默认用out即用了trunc。为
避免截断
,可指定app模式,使数据追加到文件末尾;或指定in模式,同时读写。 ate
和binary
模式可用于任何文件流对象,且可与其他任何模式组合
- 只可对ofstream或fstream对象设定
- 每个文件流类型都定义了默认的文件模式:
ifstream
关联的文件默认以in模式打开ofstream
关联的文件默认以out模式打开fstream
关联的文件默认以in和out模式打开
以 out 模式打开文件会丢弃已有数据模式
- 默认方式(out模式)打开ostream时,文件会被丢弃。因为out模式意味着同时使用trunc模式。保留已有数据的方法是显式指定
app
或in
模式 - 同一个流,每次用open关联到不同文件时,都可改变模式
//以下3条等价,都会截断file1
ofstream out("file1");
ofstream out("file1", ofstream::out);
ofstream out("file1", ofstream::out|ofstream::trunc);
//以下2条等价,为保留文件内容,显式指定app模式
ofstream out("file2", ofstream::app);
ofstream out("file2", ofstream::out|ofstream::app);
每次调用 open 时都会确定文件模式
- 对于一个给定流,每当打开文件时,都可以改变其文件模式
ofstream out; //未指定文件打开模式
out.open("scratchpad"); //隐含设置输出和截断
out.close(); //关闭,以便用于其他文件
out.open("precious",ofstream::app); //模式为输出和追加,即保留原有的数据
out.close()
8.3 string 流
- 头文件
sstream
定义了3个类型来支持内存IO,它们可读写string:istringstream
从string读数据ostringstream
向string写数据stringstream
既可读string又可写string
- 头文件sstream中定义的类型都继承自iostream中对应的类型
- 表8.5是sstream中定义的类型的特有操作
使用istringstream
- 用
getline
逐行读取,每次读到的整行文本用istringstream
读取单词
// will hold a line and word from input, respectively
string line, word;
// will hold all the records from the input
vector<PersonInfo> people;
// read the input a line at a time until end-of-file (or other error)
while (getline(is, line)) {
PersonInfo info; // object to hold this record's data
istringstream record(line); // bind record to the line we just read
record >> info.name; // read the name
while (record >> word) // read the phone numbers
info.phones.push_back(word); // and store them
people.push_back(info); // append this record to people
}
使用ostringstream
- 用
ostringstream
逐步构造输出,最后一起打印
ostringstream os;
for (const auto &entry : people) { // 对 people 中每一项
ostringstream formatted, badNums; // 每个循环步创建的对象
for (const auto &nums : entry.phones) { // 对每个数
if (!valid(nums))
badNums << " " << nums; // 将数的字符串形式存入 badNums
else
// 将格式化的字符串"写入" formatted
formatted << " " << format(nums);
}
if (badNums.str().empty()) // 没有错误的数
// 打印名字和格式化的数
os << entry.name << " " << formatted.str() << endl;
else
// 否则,打印名字和错误的数
cerr << "input error: " << entry.name
<< " invalid numbers(s) " << badNums.str() << endl;
}
cout << os.str() << endl;
【c++ Prime 学习笔记】第8章 IO库的更多相关文章
- 《C++ Primer》笔记 第8章 IO库
iostream定义了用于读写流的基本类型,fstream定义了读写命名文件的类型,sstream定义了读写内存string对象的类型. 标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制 ...
- 学习 primer 第8章 IO库 小结
iostream处理控制台IO fstream处理命名文件IO stringstream完成内存string的IO 非常重要!!!!!!!!!! ========================== ...
- Stealth视频教程学习笔记(第二章)
Stealth视频教程学习笔记(第二章) 本文是对Unity官方视频教程Stealth的学习笔记.在此之前,本人整理了Stealth视频的英文字幕,并放到了优酷上.本文将分别对各个视频进行学习总结,提 ...
- Stealth视频教程学习笔记(第一章)
Stealth视频教程学习笔记(第一章) 本文是对Unity官方视频教程Stealth的学习笔记.在此之前,本人整理了Stealth视频的英文字幕,并放到了优酷上.本文将分别对各个视频进行学习总结,提 ...
- 20145330《Java学习笔记》第一章课后练习8知识总结以及IDEA初次尝试
20145330<Java学习笔记>第一章课后练习8知识总结以及IDEA初次尝试 题目: 如果C:\workspace\Hello\src中有Main.java如下: package cc ...
- java JDK8 学习笔记——第16章 整合数据库
第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ...
- CSS3秘笈第三版涵盖HTML5学习笔记1~5章
第一部分----CSS基础知识 第1章,CSS需要的HTML HTML越简单,对搜索引擎越友好 div是块级元素,span是行内元素 <section>标签包含一组相关的内容,就像一本书中 ...
- 《DOM Scripting》学习笔记-——第三章 DOM
<Dom Scripting>学习笔记 第三章 DOM 本章内容: 1.节点的概念. 2.四个DOM方法:getElementById, getElementsByTagName, get ...
- Programming Entity Framework-dbContext 学习笔记第五章
### Programming Entity Framework-dbContext 学习笔记 第五章 将图表添加到Context中的方式及容易出现的错误 方法 结果 警告 Add Root 图标中的 ...
- The Road to learn React书籍学习笔记(第三章)
The Road to learn React书籍学习笔记(第三章) 代码详情 声明周期方法 通过之前的学习,可以了解到ES6 类组件中的生命周期方法 constructor() 和 render() ...
随机推荐
- 关于 antd tree 组件的推拽操作
最近项目中使用到 tree 组件的推拽操作, 按常理来说应该主要用到其中的 onDrop 事件,但其中的参数又没有详细的说明,只是在官网给了个例子,网上搜索后又没有发现到位的总结. 因此经过N次的测试 ...
- .NET 5 支持 Azure Functions OpenAPI 扩展啦
今年5月,在 Build大会上,Azure FunctionsOpenAPI的功能支持(预览版)正式宣布. 当时,它最高支持 v3 运行时--.NET Core 3.1 版本. 最近,它发布了 .NE ...
- SpringBoot详解(一)——
https://www.cnblogs.com/lifullmoon/p/14957771.html https://www.cnblogs.com/lifullmoon/p/14957751.htm ...
- adb 常用命令大全(5)- 日志相关
前言 Android 系统的日志分为两部分 底层的 Linux 内核日志输出到 /proc/kmsg Android 的日志输出到 /dev/log 语法格式 adb logcat [<opti ...
- SQL-INSERT触发器练习
&练习一 有这样的一个基础表A,字段包括:id.type.value.create_time,主要是记录某个类型的状态变化时间和值.在插入类型(type)为'runtime' 的数据时,根据前 ...
- k8s架构与组件详解
没有那么多花里胡哨,直接进行一个K8s架构与组件的学习. 一.K8s架构 k8s系统在设计是遵循c-s架构的,也就是我们图中apiserver与其余组件的交互.在生产中通常会有多个Master以实现K ...
- leetcode数组典型题目小结
数组与矩阵 数组与矩阵的基本知识: 1.数组:数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式. 首先,数组会利用索引来记录每个元素在数组中的位置,且在大多数 ...
- Linux-实战常用命令
目录 关机/重启/注销 系统信息和性能查看 磁盘和分区 ⽤户和⽤户组 ⽹络和进程管理 常⻅系统服务命令 ⽂件和⽬录操作 ⽂件查看和处理 打包和解压 RPM包管理命令 YUM包管理命令 DPKG包管理命 ...
- 【转】Linux 查看端口占用情况
Linux 查看端口占用情况可以使用 lsof 和 netstat 命令. lsof lsof(list open files)是一个列出当前系统打开文件的工具. lsof 查看端口占用语法格式: l ...
- vue项目实现文件下载进度条
平时业务中下载文件方式常见的有俩种: 第一种,直接访问服务器的文件地址,自动下载文件: 第二种 ,服务器返回blob文件流,再对文件流进行处理和下载. 一般小文件适用于第一种下载方案,不占用过多服务器 ...