Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[debutant] boucle while et fscanf

17 réponses
Avatar
Brice
Bonjour

je suis en train de débuter en c et je regarde actuellement comment on
peut accéder à des fichiers en c.

J'ai écrit le fichier suivant:

[brice@TuxBox:essais]$ cat freejazz.txt
Coltrane John 1967
Ayler Albert 1970
Dolphy Eric 1964



mon programme doit simplement lire ce fichier et renvoyé Nom Prenom nous
a quitté en Date.

J'ai testé deux codes différents qui me donent des résultats différents
mais je ne vois pas pourquoi. Voici les codes et les resultats:


[brice@TuxBox:essais]$ cat freejazz.c
#include<stdio.h>

int main()
{
char nom[100], prenom[100];
int date;
FILE *file=fopen("freejazz.txt","r");

while ( fscanf(file,"%s %s %d",nom,prenom,&date)!=EOF )
{
printf("%s %s nous a quitté en %d\n",nom,prenom,date);
}
fclose(file);
}
[brice@TuxBox:essais]$ ./freejazz
Coltrane John nous a quitté en 1967
Ayler Albert nous a quitté en 1970
Dolphy Eric nous a quitté en 1964



celui-là marche nickel par contre:

[brice@TuxBox:essais]$ cat freejazz.c
#include<stdio.h>

int main()
{
char nom[100], prenom[100];
int date;
FILE *file=fopen("freejazz.txt","r");

while ( ! feof(file) )
{
fscanf(file,"%s %s %d",nom,prenom,&date);
printf("%s %s nous a quitté en %d\n",nom,prenom,date);
}
fclose(file);
}
[brice@TuxBox:essais]$ ./freejazz
Coltrane John nous a quitté en 1967
Ayler Albert nous a quitté en 1970
Dolphy Eric nous a quitté en 1964
Dolphy Eric nous a quitté en 1964


là j'ai un doublon sur la deuxième ligne mais je ne comprend pas
pourquoi!

Si quelqu'un a une idée je suis preneur!

ps: ok pour les puristes il faudrait que je rajoute Return 0; à la
fin... désolé -:)


--
Brice
Debian GNU/Linux testing (Linux user nb. 372699)
-----
"Unix IS user friendly, it is just selective about who his friends are"

10 réponses

1 2
Avatar
Stephane Zuckerman
[:essais]$ cat freejazz.c
#include<stdio.h>

int main()
{
char nom[100], prenom[100];
int date;
FILE *file=fopen("freejazz.txt","r");

while ( ! feof(file) )
{
fscanf(file,"%s %s %d",nom,prenom,&date);
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}
fclose(file);
}
[:essais]$ ./freejazz
Coltrane John nous a quitté en 1967
Ayler Albert nous a quitté en 1970
Dolphy Eric nous a quitté en 1964
Dolphy Eric nous a quitté en 1964


là j'ai un doublon sur la deuxième ligne mais je ne comprend pas
pourquoi!


avec
fscanf(file,"%s %s %dn",nom,prenom,&date);
ça marche. Ton buffer n'est pas "flushé" correctement, car il reste un
'n' coincé dedans. Maintenant, l'explication totalement exacte, je suis
incapable de te la donner : tu as eu la version "en gros". :-) Les vrais
experts te répondront mieux que moi sur le pourquoi du comment. Cela dit,
il y a des chances que tu trouves la solution dans la FAQ de (f)clc.

Pour le "return 0;" en fin de main(), ce n'est pas être puriste, c'est
être cohérent et rigoureux. [1]

Sinon, ton fscanf() est dangereux (mais ça a déjà été dit sur cfou ;) ).


[1] Oui oui, je sais, le C99 permet d'omettre le return en fin de main...
--
"Je deteste les ordinateurs : ils font toujours ce que je dis, jamais ce
que je veux !"
"The obvious mathematical breakthrough would be development of an easy
way to factor large prime numbers." (Bill Gates, The Road Ahead)

Avatar
Antoine Leca
En <news:,
Stephane Zuckerman va escriure:
avec
fscanf(file,"%s %s %dn",nom,prenom,&date);
ça marche.


Ne marche pas s'il manque un n sur la dernière ligne (je sais, ce n'est pas
conforme à la norme, mais cela arrive).


Antoine

Avatar
Richard Delorme

#include<stdio.h>

int main()
{
char nom[100], prenom[100];
int date;
FILE *file=fopen("freejazz.txt","r");

while ( fscanf(file,"%s %s %d",nom,prenom,&date)!=EOF )
{
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}
fclose(file);
}
[:essais]$ ./freejazz
Coltrane John nous a quitté en 1967
Ayler Albert nous a quitté en 1970
Dolphy Eric nous a quitté en 1964


vs :

#include<stdio.h>

int main()
{
char nom[100], prenom[100];
int date;
FILE *file=fopen("freejazz.txt","r");

while ( ! feof(file) )
{
fscanf(file,"%s %s %d",nom,prenom,&date);
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}
fclose(file);
}
[:essais]$ ./freejazz
Coltrane John nous a quitté en 1967
Ayler Albert nous a quitté en 1970
Dolphy Eric nous a quitté en 1964
Dolphy Eric nous a quitté en 1964

là j'ai un doublon sur la deuxième ligne mais je ne comprend pas
pourquoi!

Si quelqu'un a une idée je suis preneur!


fscanf renvoie EOF avant toute conversion et feof(file) est vrai
uniquement après que fscanf renvoie EOF (ou lit incomplètement les
données). Donc dans le premier cas, la boucle s'arrête quand fscanf
renvoie EOF, et dans le second cas, ce fscanf renvoyant EOF est appelé
inutilement, et comme il ne modifie pas ses arguments, l'affichage de la
dernière ligne apparaît deux fois.

Cela dit, aucune des solutions n'est satisfaisante. Des boucles
possibles seraient plutôt :
while (fscanf(file,"%s %s %d",nom,prenom,&date) == 3)
{
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}

ou

while (!feof(file) && !ferror(file))
{
if (fscanf(file,"%s %s %d",nom,prenom,&date) == 3)
{
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}
}

La première lit le fichier et s'arrête dès que la trilogie <chaine>
<chaine> <int> ne peut être lu. La seconde lit tout le fichier mais
n'affiche que les données ayant les champs <chaine> <chaine> <int>. En
cas d'erreur de données dans le fichier, elle a néanmoins toutes les
chances de connaître des problèmes de synchronisation. Il serait plus
sage de lire le fichier ligne par ligne et traiter chaque ligne avec
sscanf :

char ligne[1000];
while (fgets(ligne, 1000, stdin)) {
if (sscanf(ligne,"%s %s %d",nom,prenom,&date) == 3)
{
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}
}

A améliorer pour lire correctement les lignes très longues, ... ;-)

ps: ok pour les puristes il faudrait que je rajoute Return 0; à la
fin... désolé -:)


Il faudrait aussi plutôt écrire int main(void).

--
Richard

Avatar
Emmanuel Delahaye
Brice wrote on 19/08/05 :
[:essais]$ cat freejazz.c
#include<stdio.h>

int main()
{
char nom[100], prenom[100];
int date;
FILE *file=fopen("freejazz.txt","r");

while ( ! feof(file) )
{
fscanf(file,"%s %s %d",nom,prenom,&date);
printf("%s %s nous a quitté en %dn",nom,prenom,date);
}
fclose(file);
}
[:essais]$ ./freejazz
Coltrane John nous a quitté en 1967
Ayler Albert nous a quitté en 1970
Dolphy Eric nous a quitté en 1964
Dolphy Eric nous a quitté en 1964

là j'ai un doublon sur la deuxième ligne mais je ne comprend pas
pourquoi!


Parce qu'on se tue à dire (et la FAQ aussi) que pour détecter la fin de
lecture, on doit tester le code retourné par la fonction de lecture
(1ere méthode OK) et non feof(), qui ne sert quà identifier la cause de
la fin de lecture qu'après qu'elle ne se soit produite.

Tout est là :

http://mapage.noos.fr/emdel/notes.htm#fichiers

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"Mal nommer les choses c'est ajouter du malheur au
monde." -- Albert Camus.

Avatar
Antoine Leca
En <news:de4ngl$j09$, Antoine Leca va escriure:
En <news:,
Stephane Zuckerman va escriure:
avec
fscanf(file,"%s %s %dn",nom,prenom,&date);
ça marche.


Ne marche pas s'il manque un n sur la dernière ligne (je sais, ce
n'est pas conforme à la norme, mais cela arrive).


Gourance de ma part, en fait si cela va fonctionner, car un caractère
d'espacement peut matcher 0 caractère (c'est une exception).
Mais bon, ce n'est pas une manière très jolie de résoudre le problème.

Antoine


Avatar
Antoine Leca
En <news:,
Brice va escriure:

Il est généralement bien vu de tester les valeurs de retour des fonctions
pour identifier d'éventuelles erreurs.

#include<stdio.h>
int main() {
char nom[100], prenom[100]; int date;
FILE *file=fopen("freejazz.txt","r");


if( !file ) { /* traitement d'erreur */ }

while ( fscanf(file,"%s %s %d",nom,prenom,&date)!=EOF ) {


while( fscanf(file, "%99s %99s %d", nom, prenom, &date) == 3 ) {

printf("%s %s nous a quitté en %dn",nom,prenom,date); }
fclose(file);


if( !feof(file) )
perror("Erreur de lecture");
return 0;

}
celui-là marche nickel par contre:
while ( ! feof(file) ) {
fscanf(file,"%s %s %d",nom,prenom,&date);
là j'ai un doublon sur la deuxième ligne mais je ne comprend pas
pourquoi!


Parce que tu n'as pas compris comment marche feof() en C (c'est différent du
Eof() du Pascal).

En C, la condition « fin de fichier » n'est pas testée explicitement ; en
particulier, feof() ne regarde pas du tout si on est arrivé à la fin du
fichier; il ne fait que rapporter l'état observé précédemment : si un appel
précédent a renvoyé EOF pour indiqué une fin de fichier, alors feof() va
être vrai (tandis que si le EOF indique une erreur, c'est ferror() qui sera
vrai).

Donc dans ton cas, le fscanf() de la ligne d'Éric (pourquoi pas d'accents ?)
Dolphy ne remarque pas qu'il est arrivé à la fin du fichier, il ne fait que
lire les 4 chiffres de 1964 (plus le caractère suivant pour s'apercevoir que
le n n'est pas un chiffre, donc il le remet pour être lu ensuite). Ensuite
(on est toujours dans la troisième boucle), on imprime l'obituaire, et on
revient au while. feof() n'est pas vrai, comme je l'explique ci-dessus, donc
on commence la quatrième boucle. Cette fois-ci, le fscanf ne récupère rien;
de plus, en essayant de récupérer le nom via le spécificateur %s, il saute
tous les caractères d'espacement, donc le n qui traînait, et note qu'il est
arrivé à la fin du fichier. Donc fscanf() retourne EOF, et ne modifie aucune
des variable, qui reste à leurs valeurs concernant Éric Dolphy. La valeur de
retour n'est pas contrôllée (tout ce message n'a comme seul et unique
objectif que d'arriver à cette fine remarque ;-)), donc on continue, et on
imprime par printf() une seconde fois. Puis on revient au while et cette
fois-ci on sort, car feof() est devenu vrai.

Remarquons en passant un truc « drôle » : si tu as une erreur
d'entrée-sortie quelconque, ton programme va boucler.


Antoine

Avatar
Antoine Leca
En <news:,
Emmanuel Delahaye va escriure:
Parce qu'on se tue à dire (et la FAQ aussi)


Ah ? La FAQ (en français) le dit ? où ?


Antoine (qui vient de chercher en pensant lui aussi...)

Avatar
Brice
On 2005-08-19, Antoine Leca wrote:
En <news:de4ngl$j09$, Antoine Leca va escriure:
En <news:,
Stephane Zuckerman va escriure:
avec
fscanf(file,"%s %s %dn",nom,prenom,&date);
ça marche.


Ne marche pas s'il manque un n sur la dernière ligne (je sais, ce
n'est pas conforme à la norme, mais cela arrive).


Gourance de ma part, en fait si cela va fonctionner, car un caractère
d'espacement peut matcher 0 caractère (c'est une exception).
Mais bon, ce n'est pas une manière très jolie de résoudre le problème.

Antoine




merci pour les réponses! c'est bizarre car cette erreur vient d'un cours
que j'ai trouvé sur le web!!

bon il faut alors que je creuse sscanf et fgets en autres...

Par contre je veux bien que fscanf puisse présenter des problèmes de
sécurité mais je vois pas pourquoi cela peut mener au buffer overflow
dans la mesure ou mes variables de type char sont déclarées avec "char
tab[N]" où je défini N, je vois mieux le risque avec "char tab[]".

--
Brice
Debian GNU/Linux testing (Linux user nb. 372699)
-----
"Unix IS user friendly, it is just selective about who his friends are"



Avatar
Serge Paccalin
Le vendredi 19 août 2005 à 14:58:42, Brice a écrit dans fr.comp.lang.c :

printf("%s %s nous a quitté en %dn",nom,prenom,date);

ps: ok pour les puristes il faudrait que je rajoute Return 0; à la
fin... désolé -:)


Il faut aussi rajouter un « s » à « quitté ».

--
___________ 19/08/2005 16:40:46
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763

Avatar
Emmanuel Delahaye
Brice wrote on 19/08/05 :
merci pour les réponses! c'est bizarre car cette erreur vient d'un cours
que j'ai trouvé sur le web!!


Mouaaarrrfff !

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"There are 10 types of people in the world today;
those that understand binary, and those that dont."

1 2