Como obter uma mensagem de erro quando a abertura do ifstream falha


99
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?


3


3
@Alex Farber: Claro. cerr << "Error code: " << strerror(errno); // Get some info as to whyparece relevante para a questão.
— Matthieu Rouget

@MatthieuRouget: Verifique a possível duplicata que postei - parece que esse é um comportamento não padrão implementado apenas pelo gcc.
— arne

1
@MatthieuRouget: strerror(errno)funciona. Poste isso como resposta, vou aceitar.
— Alex F

Respostas:


72

Cada chamada de sistema que falha atualiza o errnovalor.

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

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

No entanto, como cada chamada de sistema atualiza o errnovalor global , você pode ter problemas em um aplicativo multithread se outra chamada de sistema disparar um erro entre a execução de f.opene o uso de errno.

No sistema com padrão POSIX:

errno é thread local; configurá-lo em um thread não afeta seu valor em nenhum outro thread.


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

e.what() parecia a princípio ser uma forma mais C ++ - idiomicamente correta de implementar isso, no entanto, a string retornada por esta função é dependente da implementação e (pelo menos no libstdc ++ do G ++) esta string não tem informações úteis sobre o motivo do erro ...


1
e.what()não parece dar muitas informações, veja atualizações na minha resposta.
— Arne Mertz de

17
errnousa armazenamento local de thread em sistemas operacionais modernos. No entanto, não há garantia de que as fstreamfunções não serão destruídas errnoapós a ocorrência de um erro. As funções subjacentes podem não ser definidas errno(chamadas de sistema diretas no Linux ou Win32). Isso não funciona em muitas implementações do mundo real.
— strcat

1
No MSVC, e.what()sempre imprime a mesma mensagem " iostream stream error"
— rustyx

warning C4996: 'strerror': This function or variable may be unsafe. Consider using strerror_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\string.h(168) : see declaration of 'strerror'
— sergiol

1
@sergiol Essas são mentiras. Ignore-os ou desative o aviso.
— SS Anne

29

Você pode tentar deixar o fluxo 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 fornece "ios_base :: failbit set" enquanto strerror(errno)fornece "Nenhum 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 funcionar para você (não sei o que dirá sobre o erro, já que 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";
}

Obrigado. Não testei isso porque strerror(errno)postado nos comentários funciona e é muito simples de usar. Acho que e.whatvai funcionar, pois errnofunciona.
— Alex F

Então veja as anotações sobre multithreading na resposta de Matthieus - meu palpite é que e.what()será o que strerrorretornará, de uma maneira threadsafe. Ambos provavelmente dependerão da plataforma.
— Arne Mertz

1
@AlexFarber: Acho que a resposta de Arne é melhor do que a minha. Minha solução não é o C ++ - maneira de resolver seu problema. No entanto, não encontrei informações oficiais sobre como a biblioteca C ++ mapeia erros de chamada de sistema para exception.what(). Pode ser uma boa oportunidade para mergulhar no código-fonte libstdc ++ :-)
— Matthieu Rouget

Tentei o seguinte: tentei abrir um arquivo não existente e a mensagem de exceção foi lida basic_ios::clear, nada mais Isso não ajuda muito. É por isso que não
— postei

@arne qual plataforma, compilador, sistema operacional?
— Arne Mertz

22

Seguindo a resposta de @Arne Mertz, a partir de C ++ 11 std::ios_base::failureherda de system_error(consulte http://www.cplusplus.com/reference/ios/ios_base/failure/ ), que contém o código de erro e a 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.se fileNamenão existir.


8
Para mim no MSVC 2015 que apenas imprime iostream stream error.
— rustyx

2
Para mim, o GCC 6.3 também imprime iostream error. Em qual compilador você testou isso? Algum compilador realmente fornece uma razão legível pelo usuário para a falha?
— Ruslan

2
Clang 6 na libc ++ no MacOS: unspecified iostream_category error.
— akim

Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) no MacOS 10.14.x: também "Erro iostream_category não especificado". strerror (errno) PARECE ser a única maneira de acertar. Acho que poderia pegá-lo primeiro perguntando ao std :: filesystem se o path.exists () e examinando o std :: error_code que ele retorna.
— SMGreenfield

7

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

Exemplo de saída (Ubuntu com 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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.