c语言ofstream头文件,c++中ifstream及ofstream超详细说明
前文说过,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超详细说明相关推荐
- 在c语言中怎么返回引用参数,C语言函数 函数的声明 C语言的头文件 #include的用法 形参和实参 函数的返回值 - 猫扑天空...
C语言函数 一.是什么? 是指编程中对一块功能代码的封装,可以理解一个程序片段 二.有什么用? 可以封装代码,方便调用,提高复用性 三.怎么用? 定义: 返回值类型 函数名(形参类型 参数名1-. ...
- c语言头文件可以定义全局变量,C语言在头文件中定义全局变量
C语言在头文件中定义全局变量 头文件定义全局变量等问题 全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 可以,在不同的C文件中以static形式来声明同名全局变量.头文件中不可以直接定 ...
- c语言程序头文件作用,C语言头文件
C语言头文件教程 C 语言的头文件一般都是 .h 做为结尾的. C语言头文件详解 语法 #include 参数 参数 描述 filename 我们需要引入的头文件的名称. 说明 C 语言的头文件一般都 ...
- c语言二叉树的头文件叫什么,西安交大朱站立数据结构——使用C语言》头文件系列——二叉树.doc...
西安交大朱站立数据结构--使用C语言>头文件系列--二叉树 本文为二叉链存储结构的二叉树操作实现,实现了二叉树的定义.插入数据.删除数据.撤销以及二叉树的打印.前序遍历.中序遍历.后序遍历等.本 ...
- c语言的所有头文件,C语言所有头文件.doc
C语言所有头文件 C语言所有头文件 stdlib.h包含了很多标准函数和标准宏定义,比如NULL, EXIT_SUCCESS等. ctype.h包含了很多类型定义 string.h包含了字符串操作相关 ...
- c语言的函数头书写标准,C语言的头文件的函数和书写方法.doc
C语言的头文件的函数和书写方法 C语言头文件作用及写法 头文件几个好处: 1,头文件可以定义所用的函数列表,方便查阅你可以调用的函数:2,头文件可以定义很多宏定义,就是一些全局静态变量的定义,在这样的 ...
- C语言的头文件和库文件(函数库)
C语言的头文件和库文件(函数库) 分类: Unix环境高级编程 2011-04-17 14:37 2576人阅读 评论(1) 收藏 举报 语言cgcclinuxunix 在C语言中,头文件提供对常量的 ...
- 用C语言输出当前文件夹中所有的文件名opendir
用C语言输出当前文件夹中所有的文件名 使用opendir() #include <stdio.h> #include <dirent.h> #include <sys/t ...
- c语言usleep头文件,C语言Sleep clock time函数的使用
1.Sleep Sleep函数的功能是让程序挂起一段时间,第一个字母大写,只在VC中使用,头文件为#include,单位为毫秒,若想挂起120毫秒,可以写为: Sleep(120); 在linux g ...
最新文章
- 2019年1月计算机书籍JavaScript新书
- NB-IOT来了,物联网时代才真正来临
- opengl地球贴纹理_一文看懂材质/纹理 Material, Texture, Shading, Shader 的区别
- SicilyRails
- Iterator接口和for...of循环
- python中文解释-python是解释型语言么
- php与数据库编码格式转码,php 数据库 中文转码
- java的基本语法(一)
- Android系统源码学习步骤
- SpringBoot整合集成p6spy
- 服务器计算技术解决远程接入速度困扰
- ECCV2022 论文 Contrastive Deep Supervision
- 70.爬楼梯(简单)
- 垃圾网线,毁我青春(ubuntu安装失败)
- 关于Java的位移运算
- Java中判断输入错误的,判断验证码输入是否正确有关问题
- Windows10下安装Git
- MATLAB---约束最小平方滤波
- Vs Code使用国际化多语言插件-i18n AllyVue i18n-插件使用
- 【搞定工作】一大波高薪工作机会拍了拍你
热门文章
- 使用Echarts找不到自带china地图?
- 正则表达式语法及用法
- php使用popover,在Bootstrap中使用popover如何实现显示隐藏功能
- 第二类贝塞尔函数讨论
- 东芝L730-T21N 升级小记
- 基于matlab的二阶锁相环,一种基于双二阶广义积分器锁相环的制作方法
- 盗梦空间:在X86平台上构建ARM模拟器
- c语言作业自动批改代码,C语言助教用于批量改作业的脚本
- 社区宽带繁忙是什么意思_ISP许可证是什么?办理ISP许可证对企业有什么要求?...
- 非阻塞connect,错误码:EINPROGRESS