Saisie avec fgets et sscanf
Le
Eric Guirbal

Bonjour,
Je viens de passer douze heures en vain à comprendre un bug concernant la
saisie d'un float avec fgets et sscanf. Voici le code et un exemple
d'execution. Je pourrais perfectionner ma saisie en m'inspirant de l'exemple
donné par E. Delahaye dans la faq, mais j'aimerais comprendre le
comportement de ma programme.
5 * Saisie des notes obtenues par un patineur et affichage de sa moyenne
6 *
7 */
8
9
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #define NB_JUGES 5
14 #define NOTE_MIN 1.0
15 #define NOTE_MAX 6.0
16
17
18 int
19 main (void)
20 {
21 float somme = 0;
22 float moyenne;
23 float note;
24 char saisie[5];
25 int i;
26
27 printf ("- Saisie des notes du patineur ");
28 for (i = 0; i < NB_JUGES; i++)
29 {
30 while (1)
31 {
32 printf ("Note du juge no.%d: ", i + 1);
33 if (fgets (saisie, sizeof(saisie), stdin) == NULL)
34 {
35 fprintf (stderr, "Fin de fichier inattendu ");
36 return (1);
37 }
38 if (sscanf (saisie, "%f", ¬e) != 1)
39 printf ("Erreur, un nombre svp");
40 else
41 {
42 if ((note >= NOTE_MIN) && (note <= NOTE_MAX))
43 break;
44 printf ("Erreur, un nombre entre %.2f et %.2f svp",
45 NOTE_MIN, NOTE_MAX);
46 }
47 printf ("debug: note = %f", note);
48 }
49 somme += note;
50 }
51 moyenne = somme / NB_JUGES;
52 printf ("Moyenne du patineur: %.2f", moyenne);
53 return EXIT_SUCCESS;
54 }
55
eric@indiana:~/informatique/langageC/seance3$ ./patineur1
- Saisie des notes du patineur
Note du juge no.1: 3.5
Note du juge no.2: 4.80
Note du juge no.3:
Erreur, un nombre svp
debug: note = 4.800000
Note du juge no.3: 3.75
Note du juge no.4:
Erreur, un nombre svp
debug: note = 3.750000
Note du juge no.4: f
Erreur, un nombre svp
debug: note = 3.750000
Note du juge no.4: 2.4682
Note du juge no.5:
Erreur, un nombre entre 1.00 et 6.00 svp
debug: note = 82.000000
Note du juge no.5: 3
Moyenne du patineur: 3.50
Merci beaucoup pour votre aide.
--
Eric
Je viens de passer douze heures en vain à comprendre un bug concernant la
saisie d'un float avec fgets et sscanf. Voici le code et un exemple
d'execution. Je pourrais perfectionner ma saisie en m'inspirant de l'exemple
donné par E. Delahaye dans la faq, mais j'aimerais comprendre le
comportement de ma programme.
5 * Saisie des notes obtenues par un patineur et affichage de sa moyenne
6 *
7 */
8
9
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #define NB_JUGES 5
14 #define NOTE_MIN 1.0
15 #define NOTE_MAX 6.0
16
17
18 int
19 main (void)
20 {
21 float somme = 0;
22 float moyenne;
23 float note;
24 char saisie[5];
25 int i;
26
27 printf ("- Saisie des notes du patineur ");
28 for (i = 0; i < NB_JUGES; i++)
29 {
30 while (1)
31 {
32 printf ("Note du juge no.%d: ", i + 1);
33 if (fgets (saisie, sizeof(saisie), stdin) == NULL)
34 {
35 fprintf (stderr, "Fin de fichier inattendu ");
36 return (1);
37 }
38 if (sscanf (saisie, "%f", ¬e) != 1)
39 printf ("Erreur, un nombre svp");
40 else
41 {
42 if ((note >= NOTE_MIN) && (note <= NOTE_MAX))
43 break;
44 printf ("Erreur, un nombre entre %.2f et %.2f svp",
45 NOTE_MIN, NOTE_MAX);
46 }
47 printf ("debug: note = %f", note);
48 }
49 somme += note;
50 }
51 moyenne = somme / NB_JUGES;
52 printf ("Moyenne du patineur: %.2f", moyenne);
53 return EXIT_SUCCESS;
54 }
55
eric@indiana:~/informatique/langageC/seance3$ ./patineur1
- Saisie des notes du patineur
Note du juge no.1: 3.5
Note du juge no.2: 4.80
Note du juge no.3:
Erreur, un nombre svp
debug: note = 4.800000
Note du juge no.3: 3.75
Note du juge no.4:
Erreur, un nombre svp
debug: note = 3.750000
Note du juge no.4: f
Erreur, un nombre svp
debug: note = 3.750000
Note du juge no.4: 2.4682
Note du juge no.5:
Erreur, un nombre entre 1.00 et 6.00 svp
debug: note = 82.000000
Note du juge no.5: 3
Moyenne du patineur: 3.50
Merci beaucoup pour votre aide.
--
Eric
Le bug est simple, et est lié a la taille du buffer de saisi.
Supposont la note 2.4682. Notre buffer fait 5 de taille. On peut donc y
faire entrer "2.46 " (ne jamais oublier le final). Toutefois il reste
82 dans le "buffer" système. Cette valeur est obtenue a l'appel suivant
de fgets().
La solution : augmenter saisie[5] a une valeur un peu plus grande.
--
MagicalTux - Developpement PHP, C et un peu de tout
http://ookoo.org/
http://mapage.noos.fr/emdel/notes.htm#saisie
http://mapage.noos.fr/emdel/notes.htm#fichiers
Pourquoi 5 ?
9.99<enter>
soit
{'9','.','9','9','n',0}
il faut 6 caractères. Si il n'y a la place qur pour 5, fgets() va
laisser le 'n'. Comme tu ne le testes pas, le prochain appel de fgets()
ne sera pas blocant...
Pourquoi est-tu si radin ? Met 8 ou 16 char et c'est OK.
--
A+
Emmanuel Delahaye
dynamiquement l'espace dont j'ai ou peux avoir besoin pour une chaine
donnée?
Par exemple la même s'il met 16 un ptit malin pourra toujours bloquer
le prog en saisissant 20 caractères par exemple.
Y'a t-il une solution pour allouer dynamiquement ces espaces en C?
Ben oui. malloc()... n
Il ne va rien bloquer si le programmeur lit les conseils d'utilisation
que j'ai donné dans articles dont j'ai fourni les liens.
On peut écrire une fonction de saisie qui alloue/réalloue dynamiquement
ce qu'il faut. Il faut écrire un petit algo qui évite de réallouer à
chaque caractère (disons, pas doublage... on commence par 8, puis on
agrandit à 16, 32 etc. selon les besoins...). C'est un exercice
'semi-avancé' courant en C... En plus, on se retrouve avec une fonction
de saisie solide et souple, ce qui est *très* pratique (penser à libérer
le bloc après usage, bien sûr, on est pas des brutes...)
Genre :
char *getline_dyn(void);
Le suffixe '_dyn' est un aide mémoire : penser à libérer le bloc...
--
A+
Emmanuel Delahaye
C'est petit de commencer à moins de 256.
Je préfère utiliser une bibliothèque comme GNU Getline, l'utilisateur
peut utiliser les touches up et down pour accéder à l'historique de ce
qu'il a déjà entré et aussi plein de choses fournies par la
bibliothèque.
En plus, ça marchait bien depuis longtemps à la fin du millénaire
dernier.
--
http://harpo.free.fr/