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.