web-dev-qa-db-fra.com

Comment scanner uniquement un entier?

Je veux que le code s'exécute jusqu'à ce que l'utilisateur entre une valeur entière.

Le code fonctionne pour les tableaux char et char.

J'ai fait ce qui suit:


#include<stdio.h>
int main()
{
    int n;
    printf("Please enter an integer: ");
    while(scanf("%d",&n) != 1)
    {
        printf("Please enter an integer: ");
        while(getchar() != '\n');
    }
    printf("You entered: %d\n",n);
    return 0;
}

Le problème est que si l'utilisateur entre une valeur flottante scanf l'acceptera.

Please enter an integer: abcd
Please enter an integer: a
Please enter an integer: 5.9
You entered: 5

Comment éviter cela?

11
user1336087
  1. Vous prenez scanf().
  2. Vous le jetez à la poubelle.
  3. Vous utilisez fgets() pour obtenir une ligne entière.
  4. Vous utilisez strtol() pour analyser la ligne comme un entier, en vérifiant si elle a consommé la ligne entière.
char *end;
char buf[LINE_MAX];

do {
     if (!fgets(buf, sizeof buf, stdin))
        break;

     // remove \n
     buf[strlen(buf) - 1] = 0;

     int n = strtol(buf, &end, 10);
} while (end != buf + strlen(buf));

Utilisez fgets et strtol,

Un pointeur sur le premier caractère suivant la représentation entière dans s est stocké dans l'objet pointé par p, si *p est différent de \n alors vous avez une mauvaise entrée.

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
    char *p, s[100];
    long n;

    while (fgets(s, sizeof(s), stdin)) {
        n = strtol(s, &p, 10);
        if (p == s || *p != '\n') {
            printf("Please enter an integer: ");
        } else break;
    }
    printf("You entered: %ld\n", n);
    return 0;
}
7
David Ranieri

Je sais comment cela peut être fait en utilisant fgets et strtol, je voudrais savoir comment cela peut être fait en utilisant scanf() (si possible).

Comme le disent les autres réponses, scanf n'est pas vraiment adapté à cela, fgets et strtol est une alternative (bien que fgets ait l'inconvénient que c'est difficile pour détecter un 0 octet dans l'entrée et impossible de dire ce qui a été entré après un 0 octet, le cas échéant).

Par souci d'exhaustivité (et en supposant qu'une entrée valide est un entier suivi d'une nouvelle ligne):

while(scanf("%d%1[\n]", &n, (char [2]){ 0 }) < 2)

Vous pouvez également utiliser %n avant et après %*1[\n] avec suppression d'affectation. Notez cependant (à partir de la page de manuel Debian ):

Ce n'est pas une conversion, bien qu'elle puisse être supprimée avec le * caractère de suppression d'affectation. La norme C dit: "Exécution d'un %n la directive n'augmente pas le nombre d'affectations retourné à la fin de l'exécution "mais le Corrigendum semble contredire cela. Il est probablement sage de ne faire aucune hypothèse sur l'effet de %n conversions sur la valeur de retour.

4
mafso

Essayez d'utiliser le modèle suivant dans scanf. Il se lira jusqu'à la fin de la ligne:

scanf("%d\n", &n)

Vous n'aurez pas besoin de la getchar() à l'intérieur de la boucle puisque scanf lira toute la ligne. Les flottants ne correspondront pas au modèle scanf et l'invite demandera à nouveau un entier.

4
vicsana1

Une solution possible consiste à y penser à l'envers: Acceptez un flottant en entrée et rejetez l'entrée si le flottant n'est pas un entier:

int n;
float f;
printf("Please enter an integer: ");
while(scanf("%f",&f)!=1 || (int)f != f)
{
    ...
}
n = f;

Bien que cela permette à l'utilisateur d'entrer quelque chose comme 12.0, ou 12e0, etc.

3
hcs

Si vous souhaitez utiliser scanf, vous pouvez faire quelque chose comme ceci:

int val;
char follow;  
int read = scanf( "%d%c", &val, &follow );

if ( read == 2 )
{
  if ( isspace( follow ) )
  {
    // input is an integer followed by whitespace, accept
  }
  else
  {
    // input is an integer followed by non-whitespace, reject
  }
}
else if ( read == 1 )
{
  // input is an integer followed by EOF, accept
}
else
{
  // input is not an integer, reject
}
3
John Bode

Il est préférable d'utiliser fgets().

Pour résoudre uniquement en utilisant scanf() pour la saisie, recherchez un int et le char suivant.

int ReadUntilEOL(void) {
  char ch;
  int count;
  while ((count = scanf("%c", &ch)) == 1 && ch != '\n')
    ; // Consume char until \n or EOF or IO error
  return count;
}

#include<stdio.h>
int main(void) {
  int n;

  for (;;) {
    printf("Please enter an integer: ");
    char NextChar = '\n';
    int count = scanf("%d%c", &n, &NextChar);
    if (count >= 1 && NextChar == '\n') 
      break;
    if (ReadUntilEOL() == EOF) 
      return 1;  // No valid input ever found
  }
  printf("You entered: %d\n", n);
  return 0;
}

Cette approche ne ré-invite pas si l'utilisateur n'entre que des espaces blancs tels que Enter.

3
chux