前文说过,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 >= 201103L

basic_filebuf(const basic_filebuf&) = delete;

//这里声明了一个入参为右值引用的移动构造函数,所谓右值引用,它允许内容从一个filebuf对象移动到另外一个filebuf对象,移动完成以后,源filebuf对象将被销毁,不能再被使用,它没有发生任何拷贝的动作

basic_filebuf(basic_filebuf&&);

#endif

#if __cplusplus >= 201103L

basic_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

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则表示文件是打开状态,否则就是关闭状态

bool

is_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

#include

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;

}

else

cout << "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函数,声明了两个有参构造函数,默认是从文件读取数据

explicit

basic_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 >= 201103L

explicit

basic_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 >= 201103L

basic_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

#include

#include

using namespace std;

int main()

{

char szIn[32] = {0};

char szIn2[32] = {0};

ifstream in("aaa.txt", ios_base::in);//aaa.txt 内容:0123456789

ifstream 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 <

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 <

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 defg

in2's conteng is 3456789

[root@mylinux ~]#

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

2.3 open和close函数

函数原型如下:bool

is_open();

bool

is_open() const;

void

open(const char* __s, ios_base::openmode __mode = ios_base::in);

void

open(const std::string& __s, ios_base::openmode __mode = ios_base::in);

void

close()

{

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::binary即可。

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

open(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 state

this->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语言ofstream头文件,c++中ifstream及ofstream超详细说明相关推荐

  1. 在c语言中怎么返回引用参数,C语言函数 函数的声明 C语言的头文件 #include的用法 形参和实参 函数的返回值 - 猫扑天空...

    C语言函数 一.是什么? 是指编程中对一块功能代码的封装,可以理解一个程序片段 二.有什么用? 可以封装代码,方便调用,提高复用性 三.怎么用? 定义:  返回值类型  函数名(形参类型 参数名1-. ...

  2. c语言头文件可以定义全局变量,C语言在头文件中定义全局变量

    C语言在头文件中定义全局变量 头文件定义全局变量等问题 全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 可以,在不同的C文件中以static形式来声明同名全局变量.头文件中不可以直接定 ...

  3. c语言程序头文件作用,C语言头文件

    C语言头文件教程 C 语言的头文件一般都是 .h 做为结尾的. C语言头文件详解 语法 #include 参数 参数 描述 filename 我们需要引入的头文件的名称. 说明 C 语言的头文件一般都 ...

  4. c语言二叉树的头文件叫什么,西安交大朱站立数据结构——使用C语言》头文件系列——二叉树.doc...

    西安交大朱站立数据结构--使用C语言>头文件系列--二叉树 本文为二叉链存储结构的二叉树操作实现,实现了二叉树的定义.插入数据.删除数据.撤销以及二叉树的打印.前序遍历.中序遍历.后序遍历等.本 ...

  5. c语言的所有头文件,C语言所有头文件.doc

    C语言所有头文件 C语言所有头文件 stdlib.h包含了很多标准函数和标准宏定义,比如NULL, EXIT_SUCCESS等. ctype.h包含了很多类型定义 string.h包含了字符串操作相关 ...

  6. c语言的函数头书写标准,C语言的头文件的函数和书写方法.doc

    C语言的头文件的函数和书写方法 C语言头文件作用及写法 头文件几个好处: 1,头文件可以定义所用的函数列表,方便查阅你可以调用的函数:2,头文件可以定义很多宏定义,就是一些全局静态变量的定义,在这样的 ...

  7. C语言的头文件和库文件(函数库)

    C语言的头文件和库文件(函数库) 分类: Unix环境高级编程 2011-04-17 14:37 2576人阅读 评论(1) 收藏 举报 语言cgcclinuxunix 在C语言中,头文件提供对常量的 ...

  8. 用C语言输出当前文件夹中所有的文件名opendir

    用C语言输出当前文件夹中所有的文件名 使用opendir() #include <stdio.h> #include <dirent.h> #include <sys/t ...

  9. c语言usleep头文件,C语言Sleep clock time函数的使用

    1.Sleep Sleep函数的功能是让程序挂起一段时间,第一个字母大写,只在VC中使用,头文件为#include,单位为毫秒,若想挂起120毫秒,可以写为: Sleep(120); 在linux g ...

最新文章

  1. 2019年1月计算机书籍JavaScript新书
  2. NB-IOT来了,物联网时代才真正来临
  3. opengl地球贴纹理_一文看懂材质/纹理 Material, Texture, Shading, Shader 的区别
  4. SicilyRails
  5. Iterator接口和for...of循环
  6. python中文解释-python是解释型语言么
  7. php与数据库编码格式转码,php 数据库 中文转码
  8. java的基本语法(一)
  9. Android系统源码学习步骤
  10. SpringBoot整合集成p6spy
  11. 服务器计算技术解决远程接入速度困扰
  12. ECCV2022 论文 Contrastive Deep Supervision
  13. 70.爬楼梯(简单)
  14. 垃圾网线,毁我青春(ubuntu安装失败)
  15. 关于Java的位移运算
  16. Java中判断输入错误的,判断验证码输入是否正确有关问题
  17. Windows10下安装Git
  18. MATLAB---约束最小平方滤波
  19. Vs Code使用国际化多语言插件-i18n AllyVue i18n-插件使用
  20. 【搞定工作】一大波高薪工作机会拍了拍你

热门文章

  1. 使用Echarts找不到自带china地图?
  2. 正则表达式语法及用法
  3. php使用popover,在Bootstrap中使用popover如何实现显示隐藏功能
  4. 第二类贝塞尔函数讨论
  5. 东芝L730-T21N 升级小记
  6. 基于matlab的二阶锁相环,一种基于双二阶广义积分器锁相环的制作方法
  7. 盗梦空间:在X86平台上构建ARM模拟器
  8. c语言作业自动批改代码,C语言助教用于批量改作业的脚本
  9. 社区宽带繁忙是什么意思_ISP许可证是什么?办理ISP许可证对企业有什么要求?...
  10. 非阻塞connect,错误码:EINPROGRESS