IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SL & STL C++ Discussion :

Gestion d'erreur suffisante sur un ifstream?


Sujet :

SL & STL C++

  1. #1
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut Gestion d'erreur suffisante sur un ifstream?
    Bonjour,

    Je sais que ça paraît une question de débutant mais je n'avais jamais utilisé la STL directement pour manipuler des fichiers.
    Après avoir passé un long moment sur la doc, je pensais utiliser cette technique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    std::ifstream  fs( _termsFilePath->c_str()  ) ;
    std::string line;
     
    if(!fs )
    {
    	//erreur
    }
     
    while( !fs.eof() )
    {
    	if(!std::getline(fs, line) )
    	{
    	      //erreur	
    	}
     
            //faire qqch avec line
    }
    Ensuite je suis tombé sur la faq C++ de dvp où on conseille cette approche :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <string>
    #include <fstream>
    #include <iostream>
     
    int main()
    {
        // le constructeur de ifstream permet d'ouvrir un fichier en lecture
        std::ifstream fichier( "fichier.txt" );
     
        if ( fichier ) // ce test échoue si le fichier n'est pas ouvert
        {
            std::string ligne; // variable contenant chaque ligne lue
     
            // cette boucle s'arrête dès qu'une erreur de lecture survient
            while ( std::getline( fichier, ligne ) )
            {
                // afficher la ligne à l'écran
                std::cout << ligne << std::endl;
            }
        }
    }
    Je me demande si avec cette dernière, il n'y a pas un risque de confondre le cas ou la lecture s'arrête normalement à cause d'un eof() ou à cause d'une erreur?

  2. #2
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,
    Pour faire la distinction entre une fin de fichier classique et une erreur il me semble qu'il faut vérifier en sortie du while si le flag badbit a été mis à 1 ou non.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    // le constructeur de ifstream permet d'ouvrir un fichier en lecture
    std::ifstream fichier( "fichier.txt" );
     
    if ( fichier )
    {
       std::string ligne; // variable contenant chaque ligne lue
        while ( std::getline( fichier, ligne ) )
       {
          // afficher la ligne à l'écran
          std::cout << ligne << std::endl;
       }
       if(fichier.bad())
       {
          // Erreur pendant la lecture (qui n'est pas une simple fin du fichier)
       }
    }

  3. #3
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    La solution de la faq a également l'avantage d'être plus jolie (enfin, si on retire le if(fs) { ... } par un if(!fs) { return; } ... pour gagner un niveau d'indentation).

  4. #4
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Oui merci mais ce n'est pas un concours d'esthétique de code ce sont juste des exemples et la question est de savoir si on passe pas à côté d'un cas d'erreur. J'ai lu plein de blabla sur les forums à ce sujet mais il semble que personne sur le net n'en ait rien à faire des erreurs lors de la lecture.
    A croire que pour tout le monde, si le fichier a été ouvert, c'est succès assuré...
    Enfin si on ose pas se fier à cela, le bad bit mentionné par Arzar serait la bonne solution on dirait pour ne pas confondre la sortie de boucle en cas d'eof et la sortie de boucle silencieuse due à une erreur...

    De mon côté j'ai opté pour un code à base d'exception :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    	std::ifstream  fs ;
    	std::string line;
     
    	fs.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
     
    	try
    	{
    		fs.open("fichier.txt");
     
    		while( std::getline(fs, line) )
    		{
    			std::cout << line << std::endl;
    		}
    	}
    	catch(std::ifstream::failure& ex)
    	{
    		//lancer exception perso
    	}
    C'est la solution que j'utilise mais mes questions initiales demeurent. C'est pour ma culture générale disons.

  5. #5
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Je me demande si avec cette dernière, il n'y a pas un risque de confondre le cas ou la lecture s'arrête normalement à cause d'un eof() ou à cause d'une erreur?
    Non car le :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while ( std::getline( fichier, ligne ) )
    renvoie false dès qu'un flag est set (badbit, eofbit or failbit), ça a le même comportement que si tu faisais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while ( std::getline( fichier, ligne ).good() )
    Donc le code de la faq est très bien Et celui de Arzar est adapté à ton cas, c'est en tout cas moins lourd et plus performant que d'utiliser des exceptions.

  6. #6
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Ca je l'ai compris mais il renvoie aussi faux si le bit eof est mis...
    Donc tu pourrais avoir ta lecture qui s'arrête au milieu du fichier sans que tu saches jamais que quelque chose s'est mal passé, c'est le risque de confondre que je mentionne dans mon premier post.

    C'est pour cela que je pensais à une solution qui teste EOF séparément...

  7. #7
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    La solution la plus efficace reste alors (les exceptions marchent aussi[monavis], mais c'est moche de les utiliser pour ça [/monavis]) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    std::ifstream ifs("...");
    if (!ifs) assert(false);
     
    std::string s;
    while (std::getline(ifs, line)) {
      // Do work
    }
     
    if (ifs.rdstate() & (std::ios::failbit | std::ios::badbit)) {
    // if (ifs.bad() || ifs.fail()) { serait moins efficace (quoi que, avec les optimisations ...)
      // On n'a pas fini le fichier
      assert(false);
    }
     
    // Tout a été parfait dans la lecture du fichier !

  8. #8
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fs.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
    Si je comprends bien le standard, il ne faut pas lancer une exception sur le bit fail, uniquement sur le bit bad. Car :

    La conversion implicite de fstream vers bool qui contrôle la sortie du while est :
    Citation Envoyé par 27.5.5.4 basic_ios flags functions
    explicit operator bool() const;
    Returns: !fail().
    et la définition de fail() :
    Citation Envoyé par 27.5.5.4 basic_ios flags functions
    bool fail() const;
    Returns: true if failbit or badbit is set in rdstate()
    Donc j'en déduit qu'en sortie du while, le bit fail est positionné à true même si la fin de fichier a été atteinte normalement. Je pense qu'il faut voir le positionnement de fail à true comme une indication que la dernière opération "extraire des caractères du flux" a échoué ce qui est forcément le cas une fois la fin du fichier atteinte.

  9. #9
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    http://www.cplusplus.com/reference/iostream/ifstream/
    La doc de fail() :

    The function returns true if either the failbit or the badbit is set. At least one of these flags is set when some error other than reaching the End-Of-File occurs during an input operation.

    failbit is generally set by an input operation when the error was related to the internal logic of the operation itself, so other operations on the stream may be possible. While badbit is generally set when the error involves the loss of integrity of the stream, which is likely to persist even if a different operation is performed on the stream. badbit can be checked independently by calling member function bad.

    This function behaves like operator!.
    Cependant si on regarde la doc de "!" ça fout le doute...

  10. #10
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    Au vu des remarques, une correction de ma proposition de solution :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    std::ifstream ifs("...");
    if (!ifs) assert(false);
     
    std::string s;
    while (std::getline(ifs, line)) {
      // Do work
    }
     
    if (!ifs.eof()) {
      // On n'a pas fini le fichier, il y a donc eu une erreur en route
      assert(false);
    }
     
    // Tout a été parfait dans la lecture du fichier !

  11. #11
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    J'ai fini par utiliser ceci :
    Il est important si les flags fail et bad sont actifs de tester la boucle sur eof sinon en fin de fichier la dernière exécution de std::getline déclenchera l'exception.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    	std::ifstream  fs ;
    	std::string line;
     
    	fs.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
     
    	try
    	{
    		fs.open("fichier.txt");
     
    		while(!fs.eof()  )
    		{
    			std::getline(fs, line);
    			std::cout << line << std::endl;
    		}
    	}
    	catch(std::ifstream::failure& ex)
    	{
    		//lancer exception perso
    	}
    Enfin voilà, pour Ekleog, je ne sais pas si une fois le fichier ouvert il y a des chances qu'on puisse être *à la fois* eof ET en erreur.
    En tout cas je suis sur le cul de voir à quel point c'est problématique et tordu alors que c'est une simple opération de base.

  12. #12
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    Ce dernier code est faux : il "lit" la dernière ligne deux fois.
    Ce n'est pas pour rien que la FAQ n'utilise pas la lecture sur eof.

    L'écriture idiomatique est bien "while(getline) {} if (!eof){}"
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  13. #13
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    Si le fichier est eof et en erreur, ça veut dire que quelqu'un a essayé de lire le fichier après eof, puisqu'une opération qui échoue ne consume pas l'entrée AFAIK.

    Du coup, il est possible de faire sans les exceptions, en utilisant le code présent à mon dernier message : dès qu'il y aura une erreur (eof ou badbit ou failbit) on sortira du while ; puis on saura par le if si on est sorti en raison d'une erreur (badbit ou failbit, par le test !eof), ou par fin de fichier normale.

    Donc, au final, la solution de la faq est quasiment bonne, si ce n'est qu'il faut vérifier à la fin si on est sorti de la boucle à cause d'une erreur ou bien à cause d'un eof.

  14. #14
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Il y a un problème en effet, voici le test :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #include <iostream>
    #include <fstream>
     
    int main()
    {
     
    	//write some data in a test file
    	std::ofstream os("fichier.txt");
     
    	for( int i = 0; i < 5; i++)
    	{
    		os << "Line " << i << std::endl;
    	}
     
    	os.close();
     
    	//read them
    	std::ifstream  fs ;
    	std::string line;
     
    	fs.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
     
    	try
    	{
    		fs.open("fichier.txt");
    		std::cout << "File opened ok" << std::endl;
     
    		while(!fs.eof()  )
    		{
    			std::getline(fs, line);
    			std::cout << line << std::endl;
    		}
     
    		std::cout << "exiting eof loop" << std::endl;
    	}
    	catch(std::ifstream::failure& ex)
    	{
    		std::cout << "exception thrown" << std::endl;
    	}
    }
    la sortie est la suivante :

    File opened ok
    Line 0
    Line 1
    Line 2
    Line 3
    Line 4
    exception thrown

    Pourtant lors de mes tests sur mon application, cela fonctionnait, la raison? la dernière ligne de mon fichier ne se terminait pas par un retour chariot. Il semblerait donc que si getline trouve un retour chariot final, le stream n'est pas marqué comme eof et la lecture suivante génère une erreur.
    Je trouve ça un peu tordu...


    Si je reprend la solution de Ekleog :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #include <iostream>
    #include <fstream>
     
    int main()
    {
     
    	//write some data in a test file
    	std::ofstream os("fichier.txt");
     
    	for( int i = 0; i < 5; i++)
    		os << "Line " << i << std::endl;
     
     
    	os.close();
     
    	//read them
    	std::ifstream  fs ;
    	std::string line;
     
    	fs.open("fichier.txt");
     
    	if (! fs.is_open() )
    	{
    		std::cout << "Error opening file" << std::endl;
    		return 1;
    	}
     
    	std::cout << "File opened ok" << std::endl;
     
    	while(std::getline(fs, line) )
    		std::cout << line << std::endl;
     
     
    	if(!fs.eof() )
    		std::cout << "Something bad happened while reading" << std::endl;
    	else
    		std::cout << "Success" << std::endl;
     
     
    }
    Il sort :

    File opened ok
    Line 0
    Line 1
    Line 2
    Line 3
    Line 4
    Success
    Ce qui effectivement semble Ok. Je pensais utiliser la version avec exception car elle me permettait de centraliser ma gestion d'erreur mais vu la façon dont cela fonctionne en interne je comprend que ça n'inspire pas confiance!

  15. #15
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 633
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Salut,

    Pour répondre à la première question, l'opérateur de conversion en booléen (celui qui permet d'écrire quelque chose comme if(file) ) ne renvoie true que si aucun bit d'erreur n'est mis à un

    Cela implique que, si ton flux est effectivement évalué à true, c'est qu'il est près à subir une opération (de lecture ou d'écriture selon le cas )

    Il n'y a donc, effectivement aucune vérification autre que if(file) à effectuer pour s'assurer que le flux est dans un état permettant d'exécuter l'action demandée

    Par contre, il peut effectivement être utile / intéressant / envisageable d'investiguer un tout petit peu afin de déterminer pour quelle raison le fichier ne présente pas un état permettant d'exécuter l'action suivante

    C'est à ce moment là que les fonctions telles que failbit(), eof() et consors interviennent

    Ceci dit, il faut comprendre que le flag eof() n'est mis à 1 qu'une fois que le symbole EOF a été dépassé.

    Ainsi, si une lecture s'arrête au symbole EOF, le flag correspondant reste malgré tout à false, ce qui implique un passage supplémentaire dans une boucle proche de while(!file.eof()) avec tous les problèmes que cela peut engendrer

    C'est la raison pour laquelle la bonne pratique consiste effectivement à tester la lecture pour la boucle et à tester si la fin de fichier a bien été dépassée en dehors afin de déterminer si on a effectivement atteint la fin du fichier (on est alors bon ) ou si la lecture s'est arrêtée prématurément (par exemple, suite à une erreur de formatage )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  16. #16
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Merci,

    La bonne façon de faire est donc d'attendre que l'opération de lecture retourne false et ensuite, dans un deuxième temps, de s'interroger pourquoi l'opération n'a pas été possible en testant les flags afin de savoir si on a rencontré une erreur ou si on a naturellement atteint la fin du fichier.

    Je suis très content d'avoir eu cette discussion ici car mes recherches sur le net ne m'ont guère permis d'en apprendre long sur la gestion d'erreur de ces flux STL.

    Merci à tous

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [AC-2003] Gestion d'erreur persistante sur minuterie
    Par fdalyon dans le forum IHM
    Réponses: 2
    Dernier message: 10/04/2010, 15h48
  2. Réponses: 4
    Dernier message: 13/09/2006, 16h53
  3. gestion d'erreur resume next sur une portion de code
    Par aarlock dans le forum Access
    Réponses: 2
    Dernier message: 02/06/2006, 15h28
  4. [PHP-JS] gestion des erreurs sur liste déroulente
    Par HwRZxLc4 dans le forum Langage
    Réponses: 9
    Dernier message: 28/05/2006, 03h21
  5. [VBA-E] Pb sur gestion des erreurs
    Par micoscas dans le forum Macros et VBA Excel
    Réponses: 19
    Dernier message: 08/03/2005, 17h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo