Cómo obtener un mensaje de error cuando ifstream open falla

ifstream f; f.open(fileName); if ( f.fail() ) { // I need error message here, like "File not found" etc. - // the reason of the failure } 

¿Cómo obtener un mensaje de error como una cadena?

Cada llamada al sistema que falla actualiza el valor errno .

Por lo tanto, puede obtener más información sobre lo que ocurre cuando falla un ifstream abierto al usar algo como:

 cerr << "Error: " << strerror(errno); 

Sin embargo, dado que cada llamada al sistema actualiza el valor errno global, puede tener problemas en una aplicación multiproceso, si otra llamada al sistema desencadena un error entre la ejecución de f.open y el uso de errno .

En sistema con estándar POSIX:

errno es thread-local; configurarlo en un hilo no afecta su valor en ningún otro hilo.


Editar (gracias a Arne Mertz y otras personas en los comentarios):

e.what() pareció al principio ser una forma más C ++ - idiomáticamente correcta de implementar esto, sin embargo la cadena devuelta por esta función depende de la implementación y (al menos en libstdc ++ de G ++) esta cadena no tiene información útil sobre el motivo el error...

Puedes intentar dejar que la transmisión arroje una excepción en caso de falla:

 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() , sin embargo, no parece ser muy útil:

  • Lo probé en Win7, Embarcadero RAD Studio 2010, donde da "ios_base :: failbit set", mientras que strerror(errno) da "No such archivo o directorio".
  • En Ubuntu 13.04, gcc 4.7.3 la excepción dice "basic_ios :: clear" (gracias a arne )

Si e.what() no funciona para usted (no sé qué le dirá sobre el error, ya que no está estandarizado), intente usar std::make_error_condition (solo 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"; } 

Siguiendo la respuesta de @Arne Mertz, a partir de C ++ 11 std::ios_base::failure hereda de system_error (ver http://www.cplusplus.com/reference/ios/ios_base/failure/ ), que contiene tanto el error código y mensaje que strerror(errno) devolvería.

 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; } 

Esto imprime No such file or directory. si fileName no existe.

También puede lanzar std::system_error como se muestra en el código de prueba a continuación. Este método parece producir un resultado más legible que f.exception(...) .

 #include  // <-- requires this #include  #include  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; } 

Ejemplo de salida (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)