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

C Discussion :

lecture d'un fichier et feof


Sujet :

C

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    1 299
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 299
    Par défaut lecture d'un fichier et feof
    Bonjour, je suis en train de reprendre un code. La fonction qui lit le fichier texte contenant les inputs est comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while(feof(file))
    {
      fgets(blabla);
    }
    Ce n'est pas très joli...

    J'ai lu http://c.developpez.com/faq/?page=fi...RS_fin_fichier.

    Pouvez-vous s'il vous plait me donner un exemple ou le code précédent ne fait pas ce que l'on croit : feof() détecte une fin de fichier alors que ce n'est pas vrai.

    Merci d'avance

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    C'est pas tant ça le problème mais plutôt de continuer dans une boucle de lecture alors que tu peux avoir une erreur. D'où, l'idéé de la faq: lire tant que pas d'erreur. Puis, traiter soit fin de fichier, soit erreur.

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    1 299
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 299
    Par défaut
    OK, je comprends mieux la nuance

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 477
    Par défaut
    Citation Envoyé par salseropom Voir le message
    Bonjour, je suis en train de reprendre un code. La fonction qui lit le fichier texte contenant les inputs est comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while(feof(file))
    {
      fgets(blabla);
    }
    Ce n'est pas très joli...

    Pouvez-vous s'il vous plait me donner un exemple ou le code précédent ne fait pas ce que l'on croit : feof() détecte une fin de fichier alors que ce n'est pas vrai.
    Je me suis effectivement fait reprendre comme un débutant sur ce piège-là. Mais déjà, ce code est erroné : il faudrait un point d'exclamation dans ta condition while avant le feof().

    Comme indiqué dans la page que tu nous donnes - et contrairement à d'autres langages - feof() n'indique pas directement si un fichier a atteint sa fin, mais si la dernière erreur est due à une fin de fichier en particulier ou pas. À y réfléchir, c'est vrai que ça se tient car quand le flux de caractères vient en fait de la console, du port série ou même d'une connexion TCP, par exemples, on ne peut pas savoir à l'avance si l'on a atteint la fin du fichier ou pas. Il faut attendre le signal. Un while(feof()) réentrerait alors dans la boucle, et le signal de fin de fichier tomberait au moment de la lecture du caractère suivant.

    Cependant, il est bien possible que ton code fonctionne quand même (modulo le point d'exclamation), car tu fais un fgets(), fonction conçue pour lire itérativement les caractères, jusqu'à tomber sur un retour à la ligne. Or, si le caractère reçu est un marqueur EOF, il est clair que fgets() ne va pas l'intégrer à la chaîne ainsi composée, contrairement à un fgetc() qui va renvoyer le résultat tel quel. Néanmoins, la fin de fichier ayant quand même été atteinte, et le code EOF renvoyé, feof() va renvoyer un signal positif.

    Ça n'en fait pas pour autant un programme valide car le problème exposé plus haut demeure. Le dernier caractère envoyé peut être une fin de ligne. Cela ferait sortir fgets(), faire faire un tour de plus à while(feof()) qui retomberait sur un nouveau fgets(), laquelle fonction serait alors obligée de renvoyer une ligne vide si le signal de fin de fichier arrive à ce moment-là. Et cette ligne vide est fantôme dans le sens où elle ne proviendrait pas du fichier original.

    Si tu fais, par exemple, un programme qui compte le nombre de lignes, ce compte serait faussé.

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    1 299
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 299
    Par défaut
    OK, merci de tes explications. Je comprends mieux maintenant pourquoi le code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while(!feof(file))
    {
      fgets(blabla);
    }
    est foireux.

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 477
    Par défaut
    Citation Envoyé par salseropom Voir le message
    OK, merci de tes explications. Je comprends mieux maintenant pourquoi le code [...] est foireux.
    Pour être tout-à-fait complet, je m'aperçois que si ce que je dis est vrai, il n'y a pas moyen pour autant de recevoir un code EOF avec fgets(). Donc, un petit tour dans la man-page s'impose :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
           char *fgets(char *s, int size, FILE *stream);
    
    RETURN VALUE
           ...
    
           gets() and fgets() return s on success, and NULL on
           error or when end of file occurs while no characters
           have been read.
    On peut donc écrire de façon sûre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char buffer [LENGTH];
    FILE * f = stdin;
    
    while (fgets(buffer,LENGTH,f)==buffer)
    {
        traitement ...
    }
    
    if (ferrof(f)) { erreur ... }

  7. #7
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    On peut donc écrire de façon sûre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char buffer [LENGTH];
    FILE * f = stdin;
    
    while (fgets(buffer,LENGTH,f)==buffer)
    {
        traitement ...
    }
    
    if (ferrof(f)) { erreur ... }
    Généralement, on utilise plutôt:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while(fgets(buffer,sizeof buffer,f) != NULL)

  8. #8
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    On peut donc écrire de façon sûre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char buffer [LENGTH];
    FILE * f = stdin;
    
    while (fgets(buffer,LENGTH,f)==buffer)
    {
        traitement ...
    }
    
    if (ferrof(f)) { erreur ... }
    Oui, enfin, la forme canonique est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    char buffer [LENGTH];
    FILE * f = stdin; /* ou fopen(..., "r"); */
     
    while (fgets (buffer, sizeof buffer, f) != NULL)
    {
        /* traitement ...*/
    }
    Ne pas trop chercher à s'en éloigner...

  9. #9
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    Oui, enfin, la forme canonique est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    char buffer [LENGTH];
    FILE * f = stdin; /* ou fopen(..., "r"); */
     
    while (fgets (buffer, sizeof buffer, f) != NULL)
    {
        /* traitement ...*/
    }
    Ne pas trop chercher à s'en éloigner...
    plutôt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    char buffer [LENGTH];
    FILE * f = stdin; /* ou fopen(..., "r"); */
     
    while (fgets (buffer, LENGTH, f) != NULL)
    {
        /* traitement ...*/
    }

  10. #10
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par nicolas.sitbon Voir le message
    plutôt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    char buffer [LENGTH];
    FILE * f = stdin; /* ou fopen(..., "r"); */
     
    while (fgets (buffer, LENGTH, f) != NULL)
    {
        /* traitement ...*/
    }
    Pourquoi pas sizeof ? C'est un tableau ...

    D'autre part, l'identificateur 'LENGTH' est trompeur, car il s'agit d'une taille... (SIZE)

  11. #11
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    Pourquoi pas sizeof ? C'est un tableau ...

    D'autre part, l'identificateur 'LENGTH' est trompeur, car il s'agit d'une taille... (SIZE)
    J'ai eu la mauvaise surprise de découvrir que la fonction fgets() n'était pas aussi parfaite que tout le monde le pensait. Je programme dans un environnement 64 bits avec une quantité de RAM non négligeable et une libc qui contrairement à la glibc ne me limite pas quand à la taille de mes allocations. Il m'est arrivé d'avoir à faire des allocations dépassant INT_MAX (je suis sur une architecture LP64), le problème est que fgets() prend en 2ème argument un int et non un size_t, quand je lui passé la taille en size_t, il y a eu overflow, je me suis retrouvé avec un nombre négatif et fgets refusait de lire quoique ce soit.
    Voilà pourquoi maintenant j'évite (sans vérification) de passer un size_t à fgets().

  12. #12
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par nicolas.sitbon Voir le message
    Voilà pourquoi maintenant j'évite (sans vérification) de passer un size_t à fgets().
    Ca se défend. C'est vrai que l'interface de fgets() n'est pas formidable avec son int au lieu de size_t...

  13. #13
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 477
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    Oui, enfin, la forme canonique est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while (fgets (buffer, sizeof buffer, f) != NULL)
    {
        /* traitement ...*/
    }
    Ne pas trop chercher à s'en éloigner...
    J'évite volontairement « != NULL » dans ce cas précis car la man page précise que la fonction renvoie le premier argument en cas de succès ou NULL en cas d'échec, à l'exclusion, donc, de toute autre valeur. Si, pour une raison quelquonque, une autre valeur est retournée (par exemple, si la bibliothèque est mise à jour), ce code de retour sera considéré comme un succès implicite au lieu d'être vu comme une anomalie et provoquer l'interruption du programme.

  14. #14
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    J'évite volontairement « != NULL » dans ce cas précis car la man page précise que la fonction renvoie le premier argument en cas de succès ou NULL en cas d'échec, à l'exclusion, donc, de toute autre valeur. Si, pour une raison quelquonque, une autre valeur est retournée (par exemple, si la bibliothèque est mise à jour), ce code de retour sera considéré comme un succès implicite au lieu d'être vu comme une anomalie et provoquer l'interruption du programme.
    Impossible. Il s'agit d'une fonction du langage C et la sémantique des fonctions ne va pas changer à ce point quand le langage évolue (principe de compatibilité ascendante).

    De toutes façons, on écrit le code pour une version donnée du langage (C90, C99) et on compte sur le fait que les mainteneurs du langage ne vont faire la révolution, sinon, on est fichus. Les enjeux sont tels que ça n'arrivera pas (décision collégiale, comité, représentant des industriels etc.)

    C'est vérifié tous les jours. On se traine des gets(), des atoi() et comme l'a très justement fait remarqué Nicolas, un type int en 2 ème paramètre de fgets(), ce qui est un grave oubli lors du passage en C90. (création du type size_t).

    Je peux parier sans risque que dans 300 ans, fgets() renverra encore NULL en cas de fin de lecture (et non 'en cas d'échec'. Une fin de fichier n'est pas un 'echec', une erreur de lecture, oui).

    (en 300 ans, la sémantique de la langue française a peu évoluée...)

    Et oui, n'importe quel autre langage disparaitra, mais pas le C, car c'est le langage de base (juste au-dessus de l'assembleur), sauf révolution technologique majeur qui ferait apparaitre autre chose que des µprocesseurs ultra-rapides massivement parallèles à plusieurs milliers de cœurs.

  15. #15
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 477
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    Impossible. Il s'agit d'une fonction du langage C et la sémantique des fonctions ne va pas changer à ce point quand le langage évolue (principe de compatibilité ascendante).
    Oui, mais ce n'est pas une raison, à mon humble avis. J'irais même jusqu'à dire que si le prototype de la fonction doit rester immuable, alors je resterais dans ce cas particulier au « == buffer » plutôt qu'au « != NULL ». Ce n'est rien d'autre que la théorie du monde clos ou ouvert ...

    En développant l'exemple plus avant, on peut envisager un cas beaucoup moins « théorique » : celui où le résultat de fgets() passe d'abord dans une variable avant d'être évalué par if(). Si le programmeur rend la variable globale, ou bien si elle est retirée du bloc et qu'une variable locale de plus haut niveau prend sa place, ou encore (en C++, cette fois), qu'une variable membre est transformée en static dans le prototype, alors cette même variable peut être utilisée plusieurs fois, spécialement s'il a plusieurs threads.

    En cas de race conditions, par exemple, ce serait un garde-fou très efficace, pour les raisons exposées dans mon précédent post. Évidemment, le problème serait dû à une erreur en amont, mais il en reste que le test de retour de fgets() a pour but de garantir la validité des données que l'on s'apprête à traiter.

    Je peux parier sans risque que dans 300 ans, fgets() renverra encore NULL en cas de fin de lecture (et non 'en cas d'échec'. Une fin de fichier n'est pas un 'echec', une erreur de lecture, oui). [...] Et oui, n'importe quel autre langage disparaitra, mais pas le C, car c'est le langage de base (juste au-dessus de l'assembleur), sauf révolution technologique majeur qui ferait apparaitre autre chose que des µprocesseurs ultra-rapides massivement parallèles à plusieurs milliers de cœurs.
    Je pense la même chose du C, mais 300 ans, c'est très risqué. Le C n'en a que 36 actuellement. Il y a 300 ans, c'était l'année 1708. Je n'irai même pas jusqu'à affirmer que dans trois siècles, on traitera encore l'information de la même façon. Bien sûr, chaque fois qu'il y a eu un nouveau concept intéressant en informatique, on a cru que ce serait une révolution et que plus personne ne reviendrait en arrière et à chaque fois on est revenu à la raison. Mais, dans ce sens là, également, avant un 20ème siècle bien avancé, personne ne traitait l'information avec de l'électronique numérique ...

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

Discussions similaires

  1. lecture fichier avec feof
    Par broumbroum dans le forum Langage
    Réponses: 3
    Dernier message: 28/11/2006, 16h15
  2. Réponses: 6
    Dernier message: 02/09/2003, 15h12
  3. Lecture et ecriture fichier .ini
    Par despe dans le forum C
    Réponses: 6
    Dernier message: 23/07/2003, 20h40
  4. [langage] Optimiser la lecture d'un fichier
    Par And_the_problem_is dans le forum Langage
    Réponses: 4
    Dernier message: 05/02/2003, 08h54
  5. [langage] Optimiser la lecture d'un fichier
    Par And_the_problem_is dans le forum Langage
    Réponses: 2
    Dernier message: 11/06/2002, 10h24

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