我正在使用C fstream来读取配置文件 .
#include <fstream>
std::ifstream my_file(my_filename);
现在,如果我传递一个目录的路径,它会默默地忽略它 . 例如 . my_file.good()
返回true,即使 my_filename
是目录 . 由于这是我的程序的意外输入,我喜欢检查它,并抛出异常 .
How do I check if a just opened fstream is a regular file, directory or stream?
我似乎无法找到一种方法:
-
从给定的ifstream获取文件描述符 .
-
使用其他一些机制在ifstream中查找此信息 .
在some forum discussion中,有人认为这两者都不可能,因为这是依赖于操作系统的,因此永远不会成为fstream C标准的一部分 .
我能想到的唯一选择是重写我的代码以完全摆脱ifstream并使用文件描述符的C方法( *fp
),以及 fstat()
:
#include <stdio.h>
#include <sys/stat.h>
FILE *fp = fopen(my_filename.c_str(), "r");
// skip code to check if fp is not NULL, and if fstat() returns != -1
struct stat fileInfo;
fstat(fileno(fp), &fileInfo);
if (!S_ISREG(fileInfo.st_mode)) {
fclose(fp);
throw std::invalid_argument(std::string("Not a regular file ") + my_filename);
}
我更喜欢fstream . 因此,我的问题 .
3 回答
有不同的方法来解决这个问题:
忽略它 . 说真的,如果目录内容作为有效配置传递,我阻止用户提供管道或类似的非文件 .
打开前检查路径 . 您可以使用
stat()
或直接使用Boost.Filesystem或类似的库 . 我不是100%确定是否在C 11中添加了类似内容 . 请注意,这会产生竞争条件,因为在您检查之后但在打开之前,某些攻击者可能会使用目录切换文件 .通常,有一些方法可以从
fstream
中检索低级句柄,在您的情况下可能是FILE*
. 还有一些方法可以从FILE*
创建iostream
(不一定是fstream
!) . 这些都是特定于实现的扩展,因此您需要一些#ifdef
魔术来定制特定于已使用的stdlibrary实现的代码 . 我敢于依赖他们的存在,即使不是你仍然可以创建一个streambuf
,如果你需要移植到一个不提供更简单方法的模糊系统 .由于IO操作的操作系统依赖性,因此认为很复杂 .
我在OS X 10.10.2,Linux 2.6.32和FreeBSD 8.2-RELEASE上尝试了一些技术(后两个是稍微旧的操作系统,我使用了一些较旧的VirtualBox VM) .
我还没有找到一种万无一失的方法 . 如果你真的想检查,请在路径上使用
stat()
,或者使用fstat()
老式的Copen()
.PSkocik建议的
seekg(0, std::ios::beg);
方法对我不起作用 .对于fstream,最好的方法是打开文件并从中读取,并等待错误 .
必须设置badbit和failbit,特别是在OS X上,以便引发所有必需的异常 . 例如 .
my_file.exceptions(std::ios::failbit | std::ios::badbit);
这也会在读取常规文件后导致文件结束(EOF)异常,这需要代码忽略这些正常异常 .
my_file.eof()
也可能因更严重的错误而设置,因此对EOF条件的检查很差 .errno
是一个更好的指标:如果引发异常,但是errno仍为0,则很可能是EOF条件 .这并非总是如此 . 在FreeBSD 8.2上,打开一个目录路径只返回二进制gobbledygook,而没有引发异常 .
这个实现似乎在我测试过的3个平台上处理得有些合理 .