Check if a fstream is either a file or directory
I'm using C++ fstream to read a config file.
#include <fstream>
std::ifstream my_file(my_filename);
Right now, if I pass the path of a directory, it silently ignores this. Eg my_file.good()
returns true, even if my_filename
is a directory. Since this is unintended input for my program, I like to check for it, and throw an exception.
How do I check if a just opened fstream is a regular file, directory or stream?
I can't seem to find a way to either:
In some forum discussion it was suggested that neither is possible because this is OS-dependant, and thus could never be part of the fstream C++ standard.
The only alternative I can think of is to rewrite my code to get rid of ifstream altogether and resort to the C-method of a file descriptor ( *fp
), along with 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);
}
I prefer fstream. Hence, my question.
void assertGoodFile(const char* fileName) {
ifstream fileOrDir(fileName);
//This will set the fail bit if fileName is a directory (or do nothing if it is already set
fileOrDir.seekg(0, ios::end);
if( !fileOrDir.good()) {
throw BadFile();
};
}
There are different approaches to solving this issue:
stat()
or directly use Boost.Filesystem or some similar library. I'm not 100% sure if anything similar was added to C++11. Note that this creates a race condition though, because after you checking but before opening, some attacker could switch the file with a directory. fstream
, in your case probably a FILE*
. There are also ways to create an iostream
(not necessarily an fstream
!) from a FILE*
. Those are always implementation-specific extensions, so you need some #ifdef
magic to tailor your code specific to the used stdlibrary implementation. I would dare to rely on their presence, even if not you can still create a streambuf
on tof of a FILE*
if you need to port to some obscure system that doesn't provide an easier way. Thinks are complicated, due to the OS-dependency of the IO-operations.
I tried a few techniques on OS X 10.10.2, Linux 2.6.32, and FreeBSD 8.2-RELEASE (the later two are slightly older OSes, I used some older VirtualBox VMs).
stat()
on the path, or the old-fashioned C open()
with fstat()
. seekg(0, std::ios::beg);
method suggested by PSkocik did not work for me. my_file.exceptions(std::ios::failbit | std::ios::badbit);
my_file.eof()
may also be set upon more serious errors, so it is a poor check for the EOF condition. errno
is a better indicator: if an exception is raised, but errno is still 0, it is most likely a EOF condition. This is the implementation that seems to handle it somewhat reasonable across the 3 platforms I've tested.
#include < iostream>
#include < fstream>
#include < cerrno>
#include < cstring>
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
std::ifstream my_file;
try {
// Ensure that my_file throws an exception if the fail or bad bit is set.
my_file.exceptions(std::ios::failbit | std::ios::badbit);
std::cout << "Read file '" << argv[i] << "'" << std::endl;
my_file.open(argv[i]);
my_file.seekg(0, std::ios::end);
} catch (std::ios_base::failure& err) {
std::cerr << " Exception during open(): " << argv[i] << ": " << strerror(errno) << std::endl;
continue;
}
try {
errno = 0; // reset errno before I/O operation.
std::string line;
while (std::getline(my_file, line))
{
std::cout << " read line" << std::endl;
// ...
}
} catch (std::ios_base::failure& err) {
if (errno == 0) {
std::cerr << " Exception during read(), but errono is 0. No real error." << std::endl;
continue; // exception is likely raised due to EOF, no real error.
}
std::cerr << " Exception during read(): " << argv[i] << ": " << strerror(errno) << std::endl;
}
}
}
链接地址: http://www.djcxy.com/p/84246.html
上一篇: 代码和私人成员
下一篇: 检查fstream是文件还是目录