Pour faire écho à ce que j'ai posté dans une autre réponse sur le C++ : Je suggère de jeter scanf()
de ne jamais l'utiliser, et d'utiliser à la place fgets()
y sscanf()
.
La raison en est qu'au moins dans les systèmes de type Unix, le terminal sur lequel votre programme CLI s'exécute effectue par défaut un traitement des données de l'utilisateur avant que votre programme ne les voie. Il met en mémoire tampon les entrées jusqu'à ce qu'une nouvelle ligne soit saisie, et permet une édition rudimentaire des lignes, comme le retour à la ligne.
Vous ne pouvez donc jamais obtenir un seul caractère à la fois, ou quelques caractères isolés, mais seulement une ligne complète. Mais ce n'est pas ce que fait l'ex. scanf("%d")
il ne traite que les chiffres, et s'arrête là et laisse le reste en mémoire tampon dans la bibliothèque C, en vue d'une prochaine utilisation. stdio
à utiliser. Si votre programme a par exemple
printf("Enter a number: ");
scanf("%d", &a);
printf("Enter a word: ");
scanf("%s", word);
et vous entrez dans la ligne 123 abcd
il complète à la fois scanf()
en une seule fois, mais seulement après un retour à la ligne. Le premier scanf()
ne revient pas lorsque l'utilisateur a appuyé sur la touche espace, même si c'est là que le numéro se termine (parce qu'à ce moment-là, la ligne est encore dans le tampon de ligne du terminal) ; et la deuxième commande scanf()
n'attend pas que vous saisissiez une autre ligne (parce que le tampon d'entrée contient déjà suffisamment de données pour remplir la fonction %s
conversion).
Ce n'est pas ce à quoi les utilisateurs s'attendent habituellement !
Au lieu de cela, ils s'attendent à ce que le fait d'appuyer sur la touche Entrée complète la saisie, et si vous appuyez sur la touche Entrée, vous obtenez soit une valeur par défaut, soit une erreur, avec éventuellement une suggestion de ne donner que la réponse, s'il vous plaît.
On ne peut pas vraiment faire cela avec scanf("%d")
. Si l'utilisateur se contente d'appuyer sur la touche "Entrée", rien ne se passe. Parce que scanf()
attend toujours le numéro. Le terminal envoie la ligne plus loin, mais votre programme ne la voit pas, car scanf()
le mange. Vous n'avez pas la possibilité de réagir à l'erreur de l'utilisateur.
Ce n'est pas non plus très utile.
C'est pourquoi je suggère d'utiliser fgets()
o getline()
pour lire une ligne complète d'entrée à la fois. Cela correspond exactement à ce que le terminal donne, et donne toujours à votre programme le contrôle après que l'utilisateur a entré une ligne. Ce que vous faites avec la ligne d'entrée dépend de vous, si vous voulez un nombre, vous pouvez utiliser atoi()
, strtol()
ou même sscanf(buf, "%d", &a)
pour analyser le nombre. sscanf()
n'a pas le même décalage que scanf()
La fonction ne peut pas attendre davantage, car le tampon qu'elle lit est limité en taille et, lorsqu'il se termine, il se termine -- la fonction ne peut pas attendre davantage.
( fscanf()
sur un fichier normal peut également convenir si le format du fichier prend en charge la façon dont il passe sur les nouvelles lignes comme sur n'importe quel espace blanc. Pour les données orientées ligne, j'utiliserais toujours fgets()
y sscanf()
.)
Donc, au lieu de ce que j'ai fait ci-dessus, utilisez quelque chose comme ceci :
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
sscanf(buf, "%d", &a);
ou, en fait, vérifier la valeur de retour de sscanf()
afin de détecter les lignes vides et les autres données non valides :
#include <stdio.h>
int main(void)
{
const int bufsize = 100;
char buf[bufsize];
int a;
int ret;
char word[bufsize];
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%d", &a);
if (ret != 1) {
fprintf(stderr, "Ok, you don't have to.\n");
return 1;
}
printf("Enter a word: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%s", word);
if (ret != 1) {
fprintf(stderr, "You make me sad.\n");
return 1;
}
printf("You entered %d and %s\n", a, word);
}
Bien sûr, si vous voulez que le programme insiste, vous pouvez créer une fonction simple pour passer en boucle sur le fichier fgets()
y sscanf()
jusqu'à ce que l'utilisateur décide de faire ce qu'on lui demande, ou de sortir immédiatement avec une erreur. Cela dépend de ce que vous pensez que votre programme devrait faire si l'utilisateur ne veut pas jouer le jeu.
Vous pourriez faire quelque chose de similaire, par exemple en faisant une boucle sur getchar()
pour lire les caractères jusqu'à une nouvelle ligne après scanf("%d")
est renvoyée, ce qui permet d'éliminer les déchets laissés dans le tampon, mais cela ne résout pas le cas où l'utilisateur appuie simplement sur la touche Entrée sur une ligne vide. Quoi qu'il en soit, fgets()
lirait jusqu'à une nouvelle ligne, de sorte que vous n'ayez pas à le faire vous-même.