C++文件操作
注:追求代码简洁,有一致的C++风格,可参阅本篇博客,若追求更高的读写效率,建议参阅C语言篇 但文章还没写
本篇文章主要研究头文件fstream
中的函数和类
目前C++文件操作主要有两种流派,一种是声明fstream
对象,另一种是分开声明ifstream
和ofstream
注意,本文代码为了简洁,都是在展开std命名空间的前提下书写
fstream的使用
先写一段示例代码
1 |
|
输出结果
这一小段代码完成了文件的两种模式的打开和读写,已经体现了fstream
的基本功能,接下来分别详细介绍成员函数
和操作符重载
fstreanm::open()
函数声明:void open(const char* filename, ios_base::openmode mode)
特别的,C++11增加了一个函数重载,第一个形参变为const string& filename
实际上,也可以通过fstream
类的构造函数来打开文件,参数与open()
函数相同
1 | fstream f("filename",ios::out); |
接下来分别介绍两个形参
filename
一般第一个形参是文件名,可以传字符串
/char*
指针,C++11还支持传string
对象
文件名没什么好说的,就是有后缀的文件要注意后缀,以及文件名要写对,勤检查
但实际上,第一个形参是文件路径
,且支持相对路径
,绝对路径我测不出来
代码示例如下
1 | f.open("this,txt",ios::in);///打开当前文件夹的文件,this.txt是文件名 |
mode
用前须知:这些mode
都存在于ios_base
的类域中,但由于ios
继承自ios_base
,混用二者皆可,本文为了简洁,指定类域时,使用ios
,如f.open("file.txt",ios::out)
上图是继承关系图,箭头指向父类
这里依然统一使用fstream
类声明一个f
对象
1 | fstream f; |
打开模式比较多,下面先放一张表
mode | stands for | 描述 |
---|---|---|
out | output | 打开文件用于写入 ,即内存->文件 ,且会完全覆盖原文件,内置的流缓存(internal stream buffer )支持输出操作(类似cout ) |
in | input | 打开文件用于读取,即文件->内存 ,内置的流缓存支持输入操作(类似cin ) |
app | append | 所有的输出操作都追加在文件末尾,向已有内容追加文本 |
trunc | truncate | 在打文件前,清除所有内容 |
binary | binary | 所有操作都以二进制 的形式,而不是文本 |
ate | at end | 输出操作在文件末尾开始 |
注,这些mode
能用|
操作符连接,同时使用这些mode
但是
-使用trunc | app
会打开失败
-使用trunc
而未使用|
连接out
,也会失败
接下来逐一介绍这些mode
out 和 in
out
是最常用的模式之一,用于覆盖
写入文件,而且当文件不存在时,会按文件名新建一个文件并执行写入操作(哪怕是空文件)
in
也是最常用的模式之一,用于只读
地读取文件,且当文件不存在时,会抛异常(这里挖个坑)
- 使用
out
时,f
对象支持<<
流插入操作符
1 | string str("123456"); |
- 使用
in
时,f
对象支持>>
流提取和作为getline
函数的参数
1 | string s1,s2; |
- 使用
in | out
时,f
同时支持以上操作
但是写入
操作又和单一个out
不一样,单out
是完全覆盖,不考虑原文件内容,而in | out
时,是不完全覆盖:从头开始覆盖,新写入的内容没有原文件长时,剩下的原文件依然保留
示例代码
1 | int main() |
但是,同时进行流插入和流提取时,文件的操作结果会比较诡异,所以并不建议对同一个文件流同时进行读写操作。
在一段f.open()
和f.close()
之间依然还是只进行读取或写入中的一种,而不要混合操作
缺省参数:其实mode
形参是有缺省参数的,正是ios_base::in | ios_base::out
,也就是说在明确只使用out
或in
的情况下,且执行覆盖写入操作时,单写一个f.open(文件名)
即可
其它mode
app
正如表格里描述的,使用app
时,写入时不会覆盖远内容,而是追加
到文件末尾。其中与out
一样,当文件不存在时,会自动创建并写入内容。(即使没内容,也会创建空文件)
1 | int main() |
trunc
因为不加out
会打开失败,所以trunc
算是个out
的修正,在以out
模式打开前,清空原文件的内容
乍一看,因为单out
是完全覆盖写入
,似乎trunc
没什么用
但是使用ios::out | ios::in | ios::trunc
时是不完全覆盖写入,所以提前清空内容还是很有意义的。(那为啥不用单out呢)
ate
全称at end
,单用ate
也会打开失败,当然,ios::ate | ios::out
没有意义,因为还是完全覆盖写入,ate
在ios::in | ios::out | ios::in
更加有用,可以从文件末尾追加内容
binary
虽然表格里是那么说了,有没有用binary
我是真测不出差别(真要用的话,另寻高就把)
但是单用binary
依然会打开失败,需要再连个out
或in
ifstream 和 ofstream
上图分别为二者的继承关系,实际上二者用起来和fstream
是基本一样的,只不过在打开文件时,一个始终自带ios::in
,另一个始终自带ios::out
读取文件流的一些方法
>>
操作符
这是最容易理解的方式,和从终端读取数据
到变量里是一样的,只要类型匹配,不一定要存到字符串里
getline()
函数
用getline
可以读取一行(即读到\n
或文件末尾EOF
)
但getline
主要有两种,存在于不同的头文件中,且参数不同
<string>中的getline
istream& getline (istream& is, string& str);
函数声明如上,第一个参数是文件流(fstream
类或istream
类都可以),第二个参数就是个string
对象
下面是一个逐行提取的例子
1 |
|
成员函数中的getline
std::istream::getline
根据继承关系,fstream
继承了来自istream
的getline
成员函数,也就是说它们的对象都能调用这一成员函数
istream& getline (char* s, streamsize n );
函数声明如上,可以看到,第一个参数是char*
,要传给它一个字符数组
,第二个则是读入字符数的最大值,当实际读入的字符数小于n
时,会自动在结尾加一个\0
讲真这个函数更像是来自C语言的函数
示例代码
1 | int main() |
get() 函数
1 | int get(); |
如上是两种常用的函数重载,均为继承来的成员函数
,逐字符提取的话就能提取到\n``\r
之类的转义字符
代码示例
1 | int main() |
小结
以上就是C++文件操作的大部分常用内容了。挖一挖确实也不少内容了,值得总结。