Como obter uma mensagem de erro quando o ifstream open falha

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

Como obter uma mensagem de erro como string?

Cada chamada de sistema que falha atualiza o valor de errno .

Assim, você pode ter mais informações sobre o que acontece quando um ifstream aberto falha usando algo como:

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

No entanto, uma vez que cada chamada de sistema atualiza o valor global de errno , você pode ter problemas em um aplicativo multithread, se outra chamada de sistema acionar um erro entre a execução do f.open e o uso de errno .

No sistema com padrão POSIX:

errno é thread-local; defini-lo em um segmento não afeta seu valor em qualquer outro segmento.


Edit (obrigado a Arne Mertz e outras pessoas nos comentários):

e.what() parecia, a princípio, uma maneira mais C ++ - idiomaticamente correta de implementar isto, entretanto a string retornada por essa function é dependente de implementação e (pelo menos na libstdc ++ do G + +) esta string não tem nenhuma informação útil sobre a razão por trás o erro...

Você poderia tentar deixar o stream lançar uma exceção em caso de falha:

 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() , no entanto, não parece ser muito útil:

  • Eu tentei no Win7, Embarcadero RAD Studio 2010, onde dá "ios_base :: failbit set" enquanto strerror(errno) dá "Nenhum tal arquivo ou diretório."
  • No Ubuntu 13.04, gcc 4.7.3 a exceção diz "basic_ios :: clear" (graças a arne )

Se e.what() não funciona para você (eu não sei o que ele vai te dizer sobre o erro, já que isso não é padronizado), tente usar std::make_error_condition (somente 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"; } 

Seguindo a resposta de @Arne Mertz, a partir de C ++ 11 std::ios_base::failure herda de system_error (veja http://www.cplusplus.com/reference/ios/ios_base/failure/ ), que contém o erro código e mensagem que strerror(errno) retornaria.

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

Isso imprime No such file or directory. if fileName não existe.

Você também pode lançar um std::system_error como mostrado no código de teste abaixo. Este método parece produzir saída mais legível 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; } 

Exemplo de saída (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)