在c++中经常需要读写文件,在打开文件进行操作之前,我们需要确保流的打开状态正常。
可以通过以下方法判断流状态正常:
#include <fstream>
ifstream fin;
fin.open("demo.txt");
// 1st attempt
if (fin.fail()) {} // open failed
// 2nd attempt
if (!fin) {} // open failed
// 3rd attempt
if (!fin.good()) {} // open failed
// 4th attempt
if (!fin.is_open()) {} // open failed
这些方法都是可以的,但是强烈建议使用第4种方法is_open。
因为它能检测出其他方式不能检测到的微妙问题。
——引自《c++ primer plus》
由于这个微妙问题与文件模式有关,所以在此先做介绍。
文件模式描述的是文件将如何被使用:读、写、追加、二进制方式等。
将流与文件关联时,都可以提供指定文件打开模式的第二个参数:
ifstream fin("bar", mode);// mode指定的模式
c++提供的文件模式如下:
常量 | 含义 |
---|---|
ios_base::in | 以读模式打开文件 |
ios_base::out | 以写模式打开文件 |
ios_base::ate | 打开文件,并移动到文件尾 |
ios_base::app | 追加到文件尾 |
ios_base::trune | 如果文件存在,则截短文件 |
ios_base::binary | 二进制文件 |
比如,以追加方式写文件时:
ofstream fout("bar", ios_base::out | ios_base::app);
由于历史原因,c++中经常会有c的代码,或者c++使用c编写的库,所以需要清楚 c++ 与 c 打开模式的对应关系:
c++模式 | c模式 | 含义 |
---|---|---|
ios_base::in | “r” | 以读模式打开文件 |
ios_base::out | “w” | 以写模式打开文件,如果文件已存在,则截短文件,同 “ios_base::out | ios_base::trune” |
ios_base::out | ios_base::app | “a” | 追加写 |
ios_base::in | ios_base::out | “r+” | 以读写模式打开,在文件允许位置写入 |
ios_base::in | ios_base::out | ios_base::trune | “w+” | 以读写模式打开,如果文件已存在,则截短文件 |
c++mode | ios_base::binary | cmode+b | 以二进制模式打开文件 |
c++mode | ios_base::ate | cmode | 以指定模式打开文件,并移动到文件尾。c中使用独立的函数调用移动到文件尾:fseek(file, 0, SEEK_END) |
注意:ios_base::ate 和 ios_base::app
都将文件指针指向文件尾,二者的区别是ios_base::app只允许将数据添加到文件尾,而ios_base::ate将指针放到文件尾。
其实,在上述四种方法中,前三种测试方法是等价的。
但是,它们无法检测出这样的情况: 以不合适的文件模式打开时导致的失败。
比如,当尝试以ios_base::in | ios_base::trune
模式打开文件时,这就是不允许的模式组合,导致打开流失败。
is_open()
方法用于检测此种故障。
以上是书上的说法,笔者感觉好奇,当以这种模式打开文件时,会出现怎样的错误情况,于是随手写了个测试程序:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream fin("d.txt", ios_base::in | ios_base::trunc);
if (!fin.good() || !fin || fin.fail())
{
cout << "the 3 ways think open file failed\n";
return 1;
}
cout << "the 3 ways don't think open file failed\n";
if (!fin.is_open())
{
cout << "is_open think open file failed\n";
return 2;
}
cout << "the 4th way don't think open file failed\n";
char ch;
while (fin.get(ch))
cout << ch;
cout << endl;
fin.clear();
fin.close();
return 0;
}
编译测试:
# 先不创建d.txt文件
% ./a.out
the 3 ways think open file failed
% touch d.txt && echo a > d.txt
% ./a.out
the 3 ways think open file failed
由此可见,前三种方式也能检测到此种故障。
is_open()是较新的c++引入的方法,它的引入肯定是存在理由的。
上述示例虽然在我的电脑上运行通过,但在其他系统上可能会如书中讲解一致。
总之,既然使用is_open方法没有副作用,只是多写了几个字符,这样能避免某些隐匿的bug,还是非常值得的。