前文说过,ifstream是继承于istream,ofstream是继承于ostream,fstream是继承于iostream类,而他们使用的缓冲区类是filebuf。

关于这些类之间的关系,有兴趣可以去查看我之前的文章:
c++标准输入输出流关系梳理

1. filebuf类介绍

filebuf类又比stringbuf类要复杂一点,毕竟是对文件进行读写,首先在它的成员变量中找到了这样一条声明:

__file_type      _M_file;

_M_file就是它里面的文件操作对象,那么探究一下__file_type到底是什么类型呢,通过跟代码发现它其实是一个类模板__basic_file的char实例,而__basic_file是围绕一个FILE类型的指针来进行操作的,而FILE这个类型大家其实就很熟悉啦,它是c语言标准库里面操作文件的标准类型,一般是叫做文件指针,所以从这里就可以看出filebuf最终其实还是基于c语言的fopen等这一套文件操作函数去进行处理的。当然啦,在linux里面fopen其实又是基于它的系统调用open函数来进行的,这个我们知道就好,就不再做展开啦。

当看到fopen的调用时,其实内心是挺惊喜的,因为fstream这一套东西以前也经常使用,但是呢,从来不知道它的底层到底是怎么实现的,这次总算是知道了一点。

1.1 filebuf类构造函数和析构函数

首先看一下filebuf的构造函数原型,如下:

//这里声明了一个无参构造函数
basic_filebuf();
#if __cplusplus >= 201103Lbasic_filebuf(const basic_filebuf&) = delete;
//这里声明了一个入参为右值引用的移动构造函数,所谓右值引用,它允许内容从一个filebuf对象移动到另外一个filebuf对象,移动完成以后,源filebuf对象将被销毁,不能再被使用,它没有发生任何拷贝的动作basic_filebuf(basic_filebuf&&);
#endif
#if __cplusplus >= 201103Lbasic_filebuf& operator=(const basic_filebuf&) = delete;
//同上,这里声明了一个移动赋值函数basic_filebuf& operator=(basic_filebuf&&);
#endif
//析构函数关闭文件
virtual~basic_filebuf(){ this->close(); }

小贴士:=delete是c++11的用法,在c++11以前要防止类对象被拷贝和赋值,就要把拷贝构造函数和赋值函数声明为private的,到了c++11就直接使用=delete来进行声明,它意味着这个函数不能被调用,否则编译就会报错。

从上面我们可以看到filebuf类是不允许拷贝和赋值的,否则可能会发生多个对象操作同一个IO缓冲,就会导致诸多的问题,它只允许进行移动,这样能保证任何时候同一个IO缓冲只有一个对象可以操作。

使用例子如下:

#include <fstream>
using namespace std;int main()
{filebuf buf;filebuf buf2(move(buf));//移动构造函数,std::move用于生成一个绑定在buf上的filebuf&&filebuf buf3 = move(buf);//移动赋值函数return 0;
}

1.2 open函数

函数原型如下:

//is_open用于判断文件是否是打开状态,返回true则表示文件是打开状态,否则就是关闭状态
boolis_open() const throw(){ return _M_file.is_open(); }
//__s表示文件名,__mode表示文件打开方式__filebuf_type*open(const char* __s, ios_base::openmode __mode);
#if __cplusplus >= 201103L
//同上,只是文件名使用了string表示而已__filebuf_type*open(const std::string& __s, ios_base::openmode __mode){ return open(__s.c_str(), __mode); }
#endif
//关闭文件__filebuf_type*close();

使用案例如下:

#include <fstream>
#include <iostream>
using namespace std;int main()
{filebuf buf;if ( buf.open("aaa.txt", ios_base::in) == nullptr ){cout << "file open failed" << endl;return -1;}if ( buf.is_open()){cout << "file is opened" << endl;}elsecout << "file is closed" << endl;buf.close();//可以调用也可以不调用,析构函数会自动关闭return 0;
}

还有其他函数这里就不多做介绍了,理论上来讲,我们并不会直接使用filebuf,因为它只是一个工具人,是幕后奉献者,是藏于暗中滴,大多数时候,我们都是直接使用ifstream和ofstream。

2. ifstream类

2.1 构造函数和析构函数

ifstream的构造函数除了默认无参构造函数以外,还基于filebuf的open函数声明了另外两个构造函数,fstream头文件中原型如下:

//默认的无参构造函数
basic_ifstream() : __istream_type(), _M_filebuf(){ this->init(&_M_filebuf); }
//基于filebuf的open函数,声明了两个有参构造函数,默认是从文件读取数据
explicitbasic_ifstream(const char* __s, ios_base::openmode __mode = ios_base::in): __istream_type(), _M_filebuf(){this->init(&_M_filebuf);this->open(__s, __mode);}#if __cplusplus >= 201103Lexplicitbasic_ifstream(const std::string& __s,ios_base::openmode __mode = ios_base::in): __istream_type(), _M_filebuf(){this->init(&_M_filebuf);this->open(__s, __mode);}//同样的,拷贝构造函数不允许使用basic_ifstream(const basic_ifstream&) = delete;basic_ifstream(basic_ifstream&& __rhs): __istream_type(std::move(__rhs)),_M_filebuf(std::move(__rhs._M_filebuf)){ __istream_type::set_rdbuf(&_M_filebuf); }
#endif~basic_ifstream(){ }
#if __cplusplus >= 201103Lbasic_ifstream&operator=(const basic_ifstream&) = delete;basic_ifstream&operator=(basic_ifstream&& __rhs){__istream_type::operator=(std::move(__rhs));_M_filebuf = std::move(__rhs._M_filebuf);return *this;}

ifstream的拷贝构造函数和赋值函数也是直接被禁用的,那么再调用有参的构造函数后,默认的文件就被打开了,无需再次调用open函数,可以看到它的析构函数是什么都没有做的,所以ifstream需要显式的调用close函数,如果不显式调用的话,filebuf对象也会自动调用析构函数关闭文件,但如果filebuf调用close失败,就没办法知道当前流的状态了。

2.2 swap和rdbuf函数

函数原型如下:

//交换两个ifstream
void swap(basic_ifstream& __rhs);
//返回一个指向当前filebuf缓冲区的指针
__filebuf_type*rdbuf() const;

一个使用案例如下:

#include <fstream>
#include <iostream>
#include <string.h>
using namespace std;int main()
{char szIn[32] = {0};char szIn2[32] = {0};ifstream in("aaa.txt", ios_base::in);//aaa.txt 内容:0123456789ifstream in2("file.txt");//使用默认打开方式, file.txt 内容abcdefg带换行符cout << "file aaa.txt size is " << in.rdbuf()->in_avail() << endl;in.read(szIn, 3);in2.read(szIn2, 3);cout << "in's content is " << szIn << endl << "in2's conteng is " << szIn2 <<endl;in.swap(in2);memset(szIn, 0x00, sizeof(szIn));memset(szIn2, 0x00, sizeof(szIn2));in.read(szIn, sizeof(szIn));in2.read(szIn2, sizeof(szIn2));cout << "in's content is " << szIn << endl << "in2's conteng is " << szIn2 <<endl;in.close();in2.close();return 0;
}

in_avail是streambuf类里面的一个函数,用于返回当前缓冲区长度。

编译后执行结果如下:

[root@mylinux ~]# ./a.out
file aaa.txt size is 10
in's content is 012
in2's conteng is abc
in's content is defgin2's conteng is 3456789
[root@mylinux ~]#

通过结果可以看到swap函数并不会重置当前读的位置,基本是按照原样交换过来的。

2.3 open和close函数

函数原型如下:

      boolis_open();boolis_open() const;voidopen(const char* __s, ios_base::openmode __mode = ios_base::in);voidopen(const std::string& __s, ios_base::openmode __mode = ios_base::in);voidclose(){if (!_M_filebuf.close())this->setstate(ios_base::failbit);}

open函数和is_open函数用法与filebuf类基本一致,这里不再多说,主要是close,我们可以看到它调用失败以后,会将当前流状态置为失败,所以标准的做法,还是显式的调用一下close函数比较好。

2.4 ofstream类和fstream类

ofstream用于往文件写入数据,除了构造和调用open函数的时候,默认的打开模式是ios_base::out,其他所有函数使用都与ifstream一模一样,且用法也是一样的,包括fstream的函数用法,也是一样的,只是fstream默认打开模式是ios_base::in | ios_base::out,其他函数的用法这里不再多说。

总之,我们要记住,如果要从文件读取数据,那么使用ifstream,如果要将数据写到文件,那么使用ofstream,如果既要读又要写,那么使用fstream。

3. stream流打开模式

前文说到文件打开方式ios_base::in和ios_base::out,实际上流打开方式不只这两种,下面就一一说明一下:

流打开模式 说明
ios_base::app 以追加方式打开文件,文件打开以后当前指针位置直接指到缓冲区最后面,总是从文件末尾写入
ios_base::ate 打开时文件指针位置指向文件末尾,但可以手动改变指针位置,进而从当前位置开始写入
ios_base::binary 文件打开后在二进制模式下进行读写,根据我的观察,需要与ios_base::in或者ios_base::out联合使用,否则打开失败
ios_base::in 以只读方式打开文件,若文件存在则默认从最开始读取,如果文件不存在,则文件会打开失败
ios_base::out 以只写方式打开文件,若文件存在,则清空文件内容,若文件不存在,则创建一个空文件
ios_base::trunc 每次打开文件都直接清空文件内容,一般用于写入

在使用的时候,以’|'分隔,例如:ios_base::in|ios_base::out

下面列几种不同场景下使用时的组合,如下:

场景 打开模式
以只读方式打开一个已存在文件 ios_base::in
以只写方式打开一个空文件(不论文件是否存在,文件打开后内容被清空) ios_base::out或者ios_base::trunc|ios_base::out
以追加方式打开一个文件并进行写入 ios_base::out|ios_base::app或者ios_base::app
以可读可写的方式打开一个文件,且不清空文件内容 ios_base::in|ios_base::out
以可读可写的方式打开一个文件,且清空文件内容 ios_base::in|ios_base::out|ios_base::trunc
以追加方式打开一个文件并进行读写 ios_base::in|ios_base::out|ios_base::app

以上场景都是针对文本文件进行读写的,如果想以同样的方式操作二进制文件,则在每个场景后面追加一个ios_base::binary即可。

这里在使用ifstream的时候要注意,不管是构造函数打开还是手动调用open函数打开文件,它在我们指定的文件模式上会另外追加一个ios_base::in,如下:

voidopen(const char* __s, ios_base::openmode __mode = ios_base::in){if (!_M_filebuf.open(__s, __mode | ios_base::in))this->setstate(ios_base::failbit);else// _GLIBCXX_RESOLVE_LIB_DEFECTS// 409. Closing an fstream should clear error statethis->clear();}

同理,ofstream则会默认追加一个ios_base::out,但fstream则没有默认追加,不指定就默认ios_base::in|ios_base::out,如果指定了模式,则以指定的为准。

不知道大家有没有发现一点,这里我们一直没有说到ios_base::ate的使用场景,是以为我始终想不出来有用这个的必要性,唯一的场景是想打开文件的时候指针指向末尾,而后续又可能从其他位置进行读写,就使用它,但在使用ofstream的时候如果指定了ios_base::ate,它又会因为默认追加的ios_base::out而清空文件,这时唯一的方法是使用fstream类,并指定模式为ios_base::ate|ios_base::in|ios_base::out,这样才既没有清空文件,也满足了我们的使用场景。

c++中ifstream及ofstream超详细说明相关推荐

  1. c语言ofstream头文件,c++中ifstream及ofstream超详细说明

    前文说过,ifstream是继承于istream,ofstream是继承于ostream,fstream是继承于iostream类,而他们使用的缓冲区类是filebuf. 关于这些类之间的关系,有兴趣 ...

  2. c语言ofstream未定义标识符,关于c++:c中ifstream及ofstream超详细说明

    前文说过,ifstream是继承于istream,ofstream是继承于ostream,fstream是继承于iostream类,而他们应用的缓冲区类是filebuf. 对于这些类之间的关系,有趣味 ...

  3. linux跑到命令行是到了哪里,在Linux命令行中使用tcpdump「超详细」

    原标题:在Linux命令行中使用tcpdump「超详细」 灵活,强大的命令行工具有助于减轻网络问题排查的痛苦. 根据我作为系统管理员的经验,我经常发现网络连接问题难以排除故障. 对于那些情况,tcpd ...

  4. win10环境下下载安装openpose(only cpu)并在pycharm中运行代码(超详细)

    win10环境下下载安装openpose(only cpu)并在pycharm中运行代码(超详细) (一)前言 (二)准备工作 (三)友情提醒 (四)详细安装步骤 1.新建文件夹 2.下载OpenPo ...

  5. c++中ostream类的超详细说明

    根据前文,ostream类是c++标准输出流的一个基类,本篇详细介绍ostream类的主要成员函数用法. 1.ostream的构造函数 从ostream头文件中截取一部分关于构造函数的声明和定义,如下 ...

  6. 在虚幻引擎4中播放视频文件超详细教程

    1.新建工程,我这里用的是4.20版本,在Content下新建文件夹Movies(必须是这个名字) 2.右键文件夹,选择show in Explorer,找到本地路径,将视频文件复制到此路径下 3.在 ...

  7. Vue中使用echarts的超详细步骤

    1.准备工作 首先在echarts官方选择好自己需要使用的图表,并配置完成 新建一个vue文件 2.正式配置 HTML代码段: 注意id内的命名与下述getElementById一致 <temp ...

  8. 2023年网络安全比赛--网络安全事件响应中职组(超详细)

    一.竞赛时间 180分钟 共计3小时 二.竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 1.黑客通过网络攻入本地服务器,通过特殊手段在系统中建立了多个异常进程,找出启动异常进程的脚本,并将其 ...

  9. 2023年网络安全比赛--网络安全应急响应中职组(超详细)

    一.竞赛时间 180分钟 共计3小时 二.竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 1.找出被黑客修改的系统别名,并将倒数第二个别名作为Flag值提交: 2.找出系统中被植入的后门用户删 ...

最新文章

  1. 测试一年多,上线就崩溃!微服务到底应该怎么测试?
  2. matlab中antoine方程应用,Antoine方程 安托因方程
  3. spring-boot的spring-cache中的扩展redis缓存的ttl和key名
  4. UVA 11149.Power of Matrix-矩阵快速幂倍增
  5. 13攻击行为是天生的
  6. 机器学习入门系列(1)--机器学习概览
  7. 【移植驱动到Linux3.4.2内核之一】移植DM9000C网卡驱动
  8. 中小机房UPS电源及环境多方式在线监控和告警方案
  9. 为什么刹车热了会失灵_刹车油只要不缺,没必要更换,真的是这样吗?
  10. python转行符-python实现readline去掉换行符 等特殊字符
  11. lombok的详细使用
  12. C#winform两个或多个panel重叠布局时如何显示一个
  13. ELASTIC-PHP + IK分词器 + THINKPHP6 初次使用 (关键词查询)
  14. masm32v12编译器自带库masm32.lib在nasm中的使用。
  15. 如何“杀”趋势杀毒网络版
  16. 关于IE非安全更新带来flash和ActivX不能激活的解决办法(zt)
  17. R语言实现SOM(自组织映射)模型(三个函数包+代码)
  18. linux c alarm系统调用入门
  19. Django項目部署到Ubuntu服務器
  20. 微指创始人任春雷携好机友踏入微商领域

热门文章

  1. COMSOL——复合材料建模
  2. 制作ISO文件 与 提取ISO文件
  3. 将ubuntu16制作成镜像或者docker
  4. 从应用到内核,分析top命令显示的进程名包含中括号[]的含义
  5. linux psycopg2,Ubuntu安装psycopg2笔记
  6. python修饰器太难搞_【Python】小说爬虫界面版(各种BUG已修复)
  7. linux系统的笔记本,Linux:开源的免费操作系统_笔记本电脑_笔记本评测-中关村在线...
  8. 函数名前加 符号的深入理解 C++
  9. linux chkconfig详解
  10. MySQL慢查询的12个原因