ifstream f;
f.open(fileName);
if ( f.fail() )
{
// I need error message here, like "File not found" etc. -
// the reason of the failure
}
How to get error message as string?
ifstream f;
f.open(fileName);
if ( f.fail() )
{
// I need error message here, like "File not found" etc. -
// the reason of the failure
}
How to get error message as string?
Every system call that fails update the errno
value.
Thus, you can have more information about what happens when a ifstream
open fails by using something like :
cerr << "Error: " << strerror(errno);
However, since every system call updates the global errno
value, you may have issues in a multithreaded application, if another system call triggers an error between the execution of the f.open
and use of errno
.
On system with POSIX standard:
errno is thread-local; setting it in one thread does not affect its value in any other thread.
Edit (thanks to Arne Mertz and other people in the comments):
e.what()
seemed at first to be a more C++-idiomatically correct way of implementing this, however the string returned by this function is implementation-dependant and (at least in G++'s libstdc++) this string has no useful information about the reason behind the error...
You could try letting the stream throw an exception on failure:
std::ifstream f;
//prepare f to throw if failbit gets set
std::ios_base::iostate exceptionMask = f.exceptions() | std::ios::failbit;
f.exceptions(exceptionMask);
try {
f.open(fileName);
}
catch (std::ios_base::failure& e) {
std::cerr << e.what() << '\n';
}
e.what()
, however, does not seem to be very helpful:
strerror(errno)
gives "No such file or directory."If e.what()
does not work for you (I don't know what it will tell you about the error, since that's not standardized), try using std::make_error_condition
(C++11 only):
catch (std::ios_base::failure& e) {
if ( e.code() == std::make_error_condition(std::io_errc::stream) )
std::cerr << "Stream error!\n";
else
std::cerr << "Unknown failure opening file.\n";
}
Following on @Arne Mertz's answer, as of C++11 std::ios_base::failure
inherits from system_error
(see http://www.cplusplus.com/reference/ios/ios_base/failure/), which contains both the error code and message that strerror(errno)
would return.
std::ifstream f;
// Set exceptions to be thrown on failure
f.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
f.open(fileName);
} catch (std::system_error& e) {
std::cerr << e.code().message() << std::endl;
}
This prints No such file or directory.
if fileName
doesn't exist.
You can also throw a std::system_error
as shown in the test code below. This method seems to produce more readable output than f.exception(...)
.
#include <exception> // <-- requires this
#include <fstream>
#include <iostream>
void process(const std::string& fileName) {
std::ifstream f;
f.open(fileName);
// after open, check f and throw std::system_error with the errno
if (!f)
throw std::system_error(errno, std::system_category(), "failed to open "+fileName);
std::clog << "opened " << fileName << std::endl;
}
int main(int argc, char* argv[]) {
try {
process(argv[1]);
} catch (const std::system_error& e) {
std::clog << e.what() << " (" << e.code() << ")" << std::endl;
}
return 0;
}
Example output (Ubuntu w/clang):
$ ./test /root/.profile
failed to open /root/.profile: Permission denied (system:13)
$ ./test missing.txt
failed to open missing.txt: No such file or directory (system:2)
$ ./test ./test
opened ./test
$ ./test $(printf '%0999x')
failed to open 000...000: File name too long (system:36)