Vous êtes sur la page 1sur 5

La fonction scanf()

Le problème principal de la fonctions scanf() est qu'elle est compliquée à utiliser


correctement. Cela vient du fait qu'elle attend des données formatées. Voici son prototype:
int scanf(const char *format, ... );

La fonction retourne le nombre de valeurs lues. Le premier paramètre est une chaîne de
formats (%d, %c, ...) et ensuite viennent les pointeurs vers les variables que l'on souhaite
remplir.
double a = 12.0;
int b = 0;
printf("Entrez un double: ");
b = scanf("%lf", &a);
printf("Vous avez entre: %lf, valeurs lues: %d", a, b);

Et là, votre scanf ne va pas aimer:


Entrez un double:
coucou
Vous avez entre: 12.000000, valeurs lues: 0
Vous voyez bien que scanf() n'a pas lu ce qui a été entré. En effet lorsque scanf() rencontre
quelque chose qui ne correspond pas au format, la fonction s'arrête. Et ce qui n'a pas été lu
reste dans le tampon qu'il faudrait donc vider.
Un autre inconvénient de la fonction scanf() est qu'on ne peut pas saisir facilement une chaîne
de caractère contenant des espaces. Voilà pourquoi il vaut mieux ne pas utiliser scanf() pour
les saisies clavier.
Les alternatives à scanf() pour récupérer une chaîne
Récupérer une chaîne
Nous allons d'abord voir les saisies de chaînes. Alors quelles fonctions va t'on utiliser ?
Et bien la première vous la connaissez, c'est la fonction fgets().
 Quoi ? Mais fgets() ne sert pas à lire une ligne d'un fichier ?
Si mais en C le flux d'entrée, stdin, est aussi considéré comme un fichier et il est ouvert
automatiquement au début du programme. La fonction fgets() va permettre de lire ce flux et
donc de récupérer les entrées clavier. Voici le prototype de cette fonction:
char* fgets(char *s, int n, FILE *stream);

La fonction fgets() prends comme premier paramètre la chaîne dans laquelle elle va placer la
saisie. Le deuxième paramètre est le nombre maximal de caractères (n-1 plus exactement) qui
seront lus et le dernier paramètre est le flux à lire. Ici se sera stdin.
Mais comment connaît-on le nombre de bits à lire ? Tout simplement en utilisant sizeof qui
permet de connaître le nombre de bytes d'une variable. Voilà un exemple d'utilisation de la
fonction fgets pour récupérer une chaîne:
#include <stdio.h>
#include <stdlib.h>
main ()
{
char chaine[20] = "";
printf("Entrez une chaine:\n");
fgets(chaine, sizeof(chaine), stdin);
printf("Chaine: %s.\n", chaine);
}
Votre console affiche:
Entrez une chaine:
Je suis un zero
Chaine: Je suis un zero
.
Ici il y a deux choses à remarquer. Premièrement, la fonction fgets permet la saisie de chaînes
contenant des espaces, ce que scanf() ne permet pas. Et deuxièmement, il y a un saut de ligne
à la fin de la chaîne. En effet, vous avez appuyé sur entrée pour saisir votre chaîne. Le
caractère est donc à la fin de la chaîne. Il y a un autre problème, on dit à la fonction fgets() de
lire 19 caractères mais rien n'empêche l'utilisateur de rentrer 50 caractères. À ce-moment là un
deuxième appel à la fonction fgets() récupérera la suite de l'entrée.
Il faut donc corriger ce problème. Pour cela il faut utiliser une fonction, la fonction clean().
void clean(const char *buffer, FILE *fp)
{
char *p = strchr(buffer,'\n');
if (p != NULL)
*p = 0;
else
{
int c;
while ((c = fgetc(fp)) != '\n' && c != EOF);
}
}

Je suppose que je dois vous expliquer cette fonction ?


Oui, bon d'accord. Alors cette fonction prend en paramètre la chaîne que vous avez récupérée
avec fgets() et le flux qui est comme précédemment stdin. Cette fonction ne retourne rien.
D'abord, on cherche le caractère \n dans la chaîne à l'aide de la fonction strchr() (donc il
faudra penser à inclure string.h). Si on trouve le \n, on le supprime de la chaîne et si on ne le
trouve pas on vide le buffer avec une boucle while. Cette boucle ne s'arrête pas tant qu'elle lit
un caractère différent de et que celui ci n'est pas le caractère de fin de flux ("Ctrl" + "Z" sur
Windows ou "Ctrl" + "D" sur GNU/Linux).
Donc voici comment récupérer correctement une chaîne de caractère en C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void clean(const char *buffer, FILE *fp);

int main ( int argc, char* argv[] )


{
char chaine[20] = "";

printf("Entrez une chaine:\n");


fgets(chaine, sizeof(chaine), stdin);
clean(chaine, stdin);
printf("Chaine: %s.\n", chaine);

return 0;
}
void clean(const char *buffer, FILE *fp)
{
char *p = strchr(buffer,'\n');
if (p != NULL)
*p = 0;
else
{
int c;
while ((c = fgetc(fp)) != '\n' && c != EOF);
}
}

Et voici le résultat:
Entrez une chaine:
Je suis un zero
Chaine: Je suis un zero.
Vous pouvez enfin dire au revoir à scanf().
Récupérer un caractère
Alors pourquoi je n'utilise pas fgets() + clean() ? C'est simple, il existe une fonction
permettant de ne récupérer qu'un seul caractère et non une chaîne. Cette fonction c'est
getchar(). Bien sur ce n'est pas si simple. Si vous n'utilisez que getchar() vous pourrez
rencontrer des problèmes. En effet, cette fonction prend le dernier caractère tapé par
l'utilisateur. Donc voyez ce code:
a = getchar();
printf("Vous avez tape la lettre %c\n", a);
b = getchar();
printf("Vous avez tape la lettre %c\n", b);

Avec ce code la console affiche:


d
Vous avez tape la lettre d
Vous avez tape la lettre
 
Vous voyez que votre programme ne vous demande pas de rentrer le deuxième caractère.
C'est parce que lors du deuxième appel à la fonction getchar() celle ci récupère le caractère
qui correspond à l'appui que vous avez fait sur la touche entrée après avoir tapé la lettre d.
Cela se produit parce que le \n saisi avec la touche  Entrée  est toujours dans le "buffer".
Il faut donc enlever ce \n du buffer. Pour cela on fait juste une boucle comme ceci:
while (getchar() != '\n');

Cela permet de vider le flux entrant. En effet, la boucle lit les caractères jusqu'à ce qu'elle ait
lu le \n. Celui ci n'est donc plus dans le buffer. Essayez le code suivant:
a = getchar();
while (getchar() != '\n');
printf("Vous avez tape la lettre %c\n", a);
b = getchar();
while (getchar() != '\n');
printf("Vous avez tape la lettre %c\n", b);
Vous pouvez voir que cette fois ci le programme vous demande bien les deux caractères:
d
Vous avez tape la lettre d
s
Vous avez tape la lettre s

Les alternatives à scanf() pour récupérer un nombre


Et bien il suffit d'utiliser des fonctions qui convertissent les chaînes en nombres. Cela tombe
bien parce qu'en C il y en a plusieurs : sscanf(), strtol(), strtod, strtoul()
strtol()
Tout d'abord le prototype de la fonction:
long int strtol(const char *str, char **endptr, int base);

La fonction strtol() permet de convertir une chaîne en un nombre de type long int. Le premier
paramètre de la fonction est la chaîne de caractère que l'on souhaite convertir, le second
paramètre contiendra l'adresse où la conversion s'est arrêtée. Enfin le dernier paramètre est la
base dans laquelle on souhaite convertir la chaîne. En général on utilise la base 10. Voici un
exemple d'utilisation de cette fonction:
#include <stdio.h>
#include <stdlib.h>

main ()
{
char chaine[10] = "255";
long int nombre = 0;
char* fin = NULL;
nombre = strtol(chaine, &fin, 10);
printf("Chaine: %s, nombre: %ld\n", chaine, nombre);

strtod()
Le prototype de la fonction:
double strtod(const char *str, char **endptr);

La fonction strtod() permet de convertir une chaîne en double. Comme pour la fonction
précédente, le premier paramètre de la fonction est la chaîne de caractère que l'on souhaite
convertir, le second paramètre contiendra l'adresse où la conversion s'est arrêtée. Un exemple
d'utilisation:
#include <stdio.h>
#include <stdlib.h>

main ()
{
char chaine[10] = "345.632";
double nombre = 0;
char* fin = NULL;
nombre = strtod(chaine, &fin);
printf("Chaine: %s, nombre: %lf\n", chaine, nombre);
}
strtoul()
Le prototype de la fonction:
unsigned long int strtoul(const char *str, char **endptr, int base);
La fonction strtoul() permet encore de convertir une chaîne en un nombre de type long int
mais cette fois il est unsigned c'est à dire qu'il doit être positif. Le premier paramètre de la
fonction est la chaîne de caractère que l'on souhaite convertir, le second paramètre contiendra
l'adresse où la conversion s'est arrêtée. Comme pour strtol() le dernier paramètre est la base
dans laquelle on souhaite convertir la chaîne. Voici un exemple d'utilisation de cette fonction:
#include <stdio.h>
#include <stdlib.h>

main ()
{
char chaine[10] = "255";
unsigned long int nombre = 0;
char* fin = NULL;

nombre = strtol(chaine, &fin, 10);


printf("Chaine: %s, nombre: %u\n", chaine, nombre);
}
Vous remarquerez que l'on utilise %u pour afficher un unsigned long int et non %ld comme
pour un long int.

sscanf()
La fonction sscanf() est très intéressante puisqu'elle peut remplacer les trois autres, voici son
prototype:
int sscanf(const char *s, const char *format, ... );

Comme son nom l'indique un peu cette fonction a une utilisation proche de celle de scanf().
Le premier paramètre est la chaîne de caractère à convertir, le deuxième est le format (%ld,
%lf, ...). Et enfin il y a un ou plusieurs paramètres représentant les nombres que l'on veut
"extraire" de la chaîne.
#include <stdio.h>
#include <stdlib.h>

main ()
{
char chaine[20] = "9999 45.56";
int nombre1 = 0;
double nombre2 = 0.0;
sscanf(chaine, "%d %lf", &nombre1, &nombre2);
printf("Chaine: %s, nombre1: %d, nombre2: %lf\n", chaine, nombre1, nombre2);
}
On obtient le résultat suivant:
Chaine: 9999 45.56, nombre1: 9999, nombre2: 45.56000
sscanf(chaine, "%d %lf", &nombre1, &nombre2);
En effet, pareil que pour scanf(), c'est un pointeur sur la variable que l'on passe à la fonction
sscanf(). En effet pour pouvoir modifier la valeur de la variable, il faut donner à la fonction
l'adresse de la variable. Si on ne donne que la variable elle-même, celle-ci sera dupliquée mais
sa valeur ne sera pas modifiée.