web-dev-qa-db-fra.com

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

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?

85
Alex F

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

Ainsi, vous pouvez avoir plus d’informations sur ce qui se passe lorsqu’un ifstream open échoue en utilisant quelque chose comme:

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

Cependant, étant donné que chaque appel système met à jour la valeur globale errno, des problèmes peuvent survenir dans une application multithread, si un autre appel système déclenche une erreur entre l'exécution du f.open et l'utilisation de errno.

Sur un système avec la norme POSIX:

errno est thread-local; le placer dans un thread n'affecte pas 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 manière plus correcte d'implémenter ceci, cependant la chaîne renvoyée par cette fonction dépend de l'implémentation et (au moins dans libstdc ++ de G ++), cette chaîne ne contient aucune information utile sur la raison derrière l'erreur ...

65
Matthieu Rouget

Vous pouvez essayer de laisser le flux générer 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() ne semble toutefois pas très utile:

  • Je l'ai essayé sur Win7, Embarcadero RAD Studio 2010 où il donne "ios_base :: failbit set" alors que strerror(errno) donne "Aucun fichier ni répertoire de ce type". "
  • Sous Ubuntu 13.04, gcc 4.7.3, l'exception dit "basic_ios :: clear" (grâce à arne )

Si e.what() ne fonctionne pas pour vous (je ne sais pas ce qu'il vous dira à propos de l'erreur, puisque ce n'est pas normalisé), 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";
}
28
Arne Mertz

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

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

Ceci affiche No such file or directory. Si fileName n'existe pas.

17
rthur

Vous pouvez également lancer un std::system_error Comme 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)
4
ɲeuroburɳ