Comment obtenir un message d'erreur lorsque ifstream open échoue

99
ifstream f;
f.open(fileName);

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

Comment obtenir un message d'erreur sous forme de chaîne?

Alex F
la source
3
duplication possible de vérification d'erreur ifstream C ++
Matthieu Rouget
3
@ Alex Farber: Bien sûr. cerr << "Error code: " << strerror(errno); // Get some info as to whysemble pertinent par rapport à la question.
Matthieu Rouget
@MatthieuRouget: Vérifiez l'éventuel doublon que j'ai posté - il semble que ce soit un comportement non standard uniquement implémenté par gcc.
arne
1
@MatthieuRouget: ça strerror(errno)marche. Postez ceci comme réponse, je l'accepterai.
Alex F du

Réponses:

72

Chaque appel système qui échoue met à jour la errnovaleur.

Ainsi, vous pouvez avoir plus d'informations sur ce qui se passe lorsqu'une ifstreamouverture échoue en utilisant quelque chose comme:

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

Cependant, comme chaque appel système met à jour la errnovaleur globale , vous pouvez rencontrer des problèmes dans une application multithread, si un autre appel système déclenche une erreur entre l'exécution de f.openet l'utilisation de errno.

Sur système avec standard POSIX:

errno est local au thread; le définir dans un thread n'affecte sa valeur dans aucun autre thread.


Edit (merci à Arne Mertz et à d'autres personnes dans les commentaires):

e.what() semblait au début être une façon plus correcte d'implémenter cela en C ++, mais la chaîne retournée par cette fonction dépend de l'implémentation et (au moins dans la libstdc ++ de G ++) cette chaîne n'a pas d'informations utiles sur la raison de l'erreur ...

Matthieu Rouget
la source
1
e.what()ne semble pas donner beaucoup d'informations, voir les mises à jour de ma réponse.
Arne Mertz
17
errnoutilise le stockage local des threads sur les systèmes d'exploitation modernes. Cependant, il n'y a aucune garantie que les fstreamfonctions ne seront pas écrasées errnoaprès un errno. Les fonctions sous-jacentes peuvent ne pas être définies errnodu tout (appels système directs sous Linux ou Win32). Cela ne fonctionne pas sur de nombreuses implémentations du monde réel.
strcat
1
Dans MSVC, affiche e.what()toujours le même message " 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 Ce sont des mensonges. Ignorez-les ou désactivez l'avertissement.
SS Anne
29

Vous pouvez essayer de laisser le flux lever une exception en cas d'échec:

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(), cependant, ne semble pas très utile:

  • Je l'ai essayé sur Win7, Embarcadero RAD Studio 2010 où il donne "ios_base :: failbit set" alors qu'il strerror(errno)donne "No such file or directory".
  • Sur Ubuntu 13.04, gcc 4.7.3 l'exception dit "basic_ios :: clear" (merci à arne )

Si e.what()cela ne fonctionne pas pour vous (je ne sais pas ce que cela vous dira sur l'erreur, car ce n'est pas standardisé), essayez d'utiliser std::make_error_condition(C ++ 11 uniquement):

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
la source
Merci. Je n'ai pas testé cela car strerror(errno)publié dans les commentaires fonctionne et très simple à utiliser. Je pense que e.whatcela fonctionnera, car cela errnofonctionne.
Alex F
Ensuite, voyez les annotations sur le multithreading dans la réponse de Matthieu - je suppose que e.what()ce sera ce qui strerrorrevient, d'une manière threadsafe. Les deux dépendront probablement de la plate-forme.
Arne Mertz le
1
@AlexFarber: Je pense que la réponse d'Arne est meilleure que la mienne. Ma solution n'est pas la manière C ++ de résoudre votre problème. Cependant, je n'ai pas trouvé d'informations officielles sur la manière dont la bibliothèque C ++ mappe les erreurs d'appel système exception.what(). Peut-être une bonne occasion de plonger dans le code source de libstdc ++ :-)
Matthieu Rouget
J'ai essayé ceci: j'ai essayé d'ouvrir un fichier inexistant et le message d'exception lu basic_ios::clear, rien d'autre. Ce n'est pas vraiment utile. C'est pourquoi je n'ai pas
posté
@arne quelle plateforme, compilateur, os?
Arne Mertz
22

Suite à la réponse de @Arne Mertz, à partir de C ++ 11 std::ios_base::failurehérite desystem_error (voir http://www.cplusplus.com/reference/ios/ios_base/failure/ ), qui contient à la fois le code d'erreur et le message qui strerror(errno)reviendrait.

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

Cela s'imprime No such file or directory.sifileName n'existe pas.

Arthur
la source
8
Pour moi dans MSVC 2015 qui imprime juste iostream stream error.
rustyx
2
Pour moi, GCC 6.3 imprime également iostream error. Sur quel compilateur avez-vous testé cela? Un compilateur fournit-il réellement une raison d'échec lisible par l'utilisateur?
Ruslan
2
Clang 6 sur libc ++ sur macOS: unspecified iostream_category error.
akim
Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) sur MacOS 10.14.x: également "Erreur iostream_category non spécifiée". strerror (errno) SEMBLE être le seul moyen d'y parvenir. Je suppose que je pourrais l'attraper en premier en demandant à std :: filesystem si path.exists (), et en examinant le std :: error_code qu'il renvoie.
SMGreenfield
7

Vous pouvez également lancer un std::system_errorcomme indiqué dans le code de test ci-dessous. Cette méthode semble produire une sortie plus lisible 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;
}

Exemple de sortie (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)
ɲeuroburɳ
la source