Как получить сообщение об ошибке, когда ifstream open терпит неудачу
ifstream f;
f.open(fileName);
if ( f.fail() )
{
// I need error message here, like "File not found" etc. -
// the reason of the failure
}
Как получить сообщение об ошибке в виде строки?
4 ответа:
каждый системный вызов, который не удается обновить
errno
значение.таким образом, вы можете иметь больше информации о том, что происходит, когда
ifstream
открыть не удается, используя что-то вроде :cerr << "Error: " << strerror(errno);
С системный вызов обновляет глобальный
errno
значение, вы можете иметь проблемы в многопоточном приложении, если другой системный вызов вызывает ошибку между исполнениемf.open
и использоватьerrno
.в системе со стандартом POSIX:
errno является локальным потоком; установка его в одном потоке не влияет на его значение в любом другом потоке.
Edit (спасибо Арне Мерцу и другим людям в комментариях):
e.what()
сначала казалось, что это более C++-идиоматически правильный способ реализации этого, однако строка, возвращаемая этой функцией, зависит от реализации и (at по крайней мере, в G++'S libstdc++) эта строка не содержит полезной информации о причине ошибки...
вы можете попробовать разрешить потоку выдавать исключение при сбое:
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()
, однако, не кажется очень полезным:
- я попробовал его на Win7, Embarcadero RAD Studio 2010, где он дает "ios_base::failbit set", тогда как
strerror(errno)
выдает "Нет такого файла или каталога."- на Ubuntu 13.04, gcc 4.7.3 исключение говорит " basic_ios:: clear "(спасибо Арне)
если
e.what()
не работает вы (я не знаю, что он скажет вам об ошибке, так как это не стандартизировано), попробуйте использоватьstd::make_error_condition
(только C++11):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"; }
после ответа @Arne Mertz, начиная с C++11
std::ios_base::failure
наследует отsystem_error
(см. http://www.cplusplus.com/reference/ios/ios_base/failure/), который содержит как код ошибки, так и сообщение о том, чтоstrerror(errno)
вернется.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; }
это выводит
No such file or directory.
еслиfileName
не существует.
вы также можете бросить
std::system_error
как показано в тестовом коде ниже. Этот метод, кажется, производит более читаемый вывод, чем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; }
пример вывода (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)