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. E.g. 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.
Use ifile. open() is mainly used to check if a file exists in the specific directory or not. In the filing, a stream refers to an abstract that signifies a method where input as well as output processes are executed. “ifile. open()” takes one argument that is the name of the file.
Opening a FileEither ofstream or fstream object may be used to open a file for writing. And ifstream object is used to open a file for reading purpose only.
ofstream is output file stream which allows you to write contents to a file. fstream allows both reading from and writing to files by default.
<fstream> library provides functions for files, and we should simply add #include <fstream> directives at the start of our program. To open a file, a filestream object should first be created. This is either an ofstream object for writing, or an ifstream object for reading.
Starting with C++17, we have the filesystem
available to us (based on boost::filesystem
). This will be much more portable than anything else in C++ (although the stat()
works very well too, I think).
You have two functions available, one returns an error code if you need it.
bool is_regular_file( const path& p );
bool is_regular_file( const path& p, error_code& ec );
You use these with the following:
#include <filesystem>
...
if(!std::filesystem::is_regular_file(path))
{
throw std::runtime_error(path + " was expected to be a regular file.");
}
...
Find more details on cppreference.
(WARNING: some compilers say they have c++17 but filesystem may still be experimental in them; i.e. GNU C++ has the full version only since g++ v8.0, for details, see this question: Link errors using <filesystem> members in C++17)
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.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();
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With