web-dev-qa-db-fra.com

Comment scanner uniquement un entier et répéter la lecture si l'utilisateur entre des caractères non numériques?

Voici le problème d'un jeune jeu avec le code C qui tente simplement d'empêcher l'utilisateur de saisir un caractère ou un entier inférieur à 0 ou supérieur à 23.

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

int main(void)
{
    const char *input;
    char *iPtr;
    int count = 0;
    int rows;

    printf("Enter an integer: ");
    scanf("%s", input);
    rows = strtol(input, &iPtr, 0);
    while( *iPtr != '\0') // Check if any character has been inserted
    {
        printf("Enter an integer between 1 and 23: ");
        scanf("%s", input);
    }
    while(0 < rows && rows < 24) // check if the user input is within the boundaries
    {
        printf("Select an integer from 1 to 23: ");
        scanf("%s", input);
    }  
    while (count != rows)  
    {  
        /* Do some stuff */  
    }  
    return 0;  
}

Je l'ai fait à mi-chemin et un petit Push Up sera apprécié. 

13
7kemZmani

Utilisez scanf("%d",&rows) au lieu de scanf("%s",input)

Cela vous permet d'obtenir directement la valeur entière de stdin sans avoir besoin de convertir en int.

Si l'utilisateur entre une chaîne contenant des caractères non numériques, vous devez nettoyer votre stdin avant la prochaine scanf("%d",&rows).

votre code pourrait ressembler à ceci:

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

int clean_stdin()
{
    while (getchar()!='\n');
    return 1;
}

int main(void)  
{ 
    int rows =0;  
    char c;
    do
    {  
        printf("\nEnter an integer from 1 to 23: ");

    } while (((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) || rows<1 || rows>23);

    return 0;  
}

Explication

1)

scanf("%d%c", &rows, &c)

Cela signifie que l'utilisateur doit entrer un entier et un caractère non numérique.

Exemple1:Si l'utilisateur entre aaddk puis ENTER, le scanf retournera 0. Rien capturé

Exemple2:Si l'utilisateur entre 45 puis ENTER, le scanf retournera 2 (2 éléments sont capturés). Ici %d capture 45 et %c capture \n

Exemple3:Si l'utilisateur entre 45aaadd puis ENTER, le scanf retournera 2 (2 éléments sont capturés). Ici %d capture 45 et %c capture a

2)

(scanf("%d%c", &rows, &c)!=2 || c!='\n')

Dans l'exemple1:cette condition est TRUE car scanf renvoie 0 (!=2)

Dans l'exemple2:cette condition est FALSE car scanf renvoie 2 et c == '\n'

Dans l'exemple3:cette condition est TRUE car scanf renvoie 2 et c == 'a' (!='\n')

3)

((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin())

clean_stdin() est toujours TRUE car la fonction renvoie toujours 1

Dans l'exemple1:Le (scanf("%d%c", &rows, &c)!=2 || c!='\n') est TRUE; la condition après le && doit donc être vérifiée pour que la clean_stdin() soit exécutée et que la condition entière soit TRUE

Dans l'exemple2:Le (scanf("%d%c", &rows, &c)!=2 || c!='\n') est FALSE et la condition après le && ne sera pas cochée (car quel que soit le résultat, la condition entière sera FALSE), de sorte que clean_stdin() ne sera pas exécuté et que la condition entière est FALSE

Dans l'exemple3:Le (scanf("%d%c", &rows, &c)!=2 || c!='\n') est TRUE, la condition après le && doit donc être vérifiée pour que la clean_stdin() soit exécutée et que la condition entière soit TRUE

Vous pouvez donc remarquer que clean_stdin() sera exécuté uniquement si l'utilisateur entre une chaîne contenant un caractère non numérique.

Et cette condition ((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) retournera FALSE uniquement si l'utilisateur entre integer et rien d'autre

Et si la condition ((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) est FALSE et que la integer est comprise entre et 1 et 23, la boucle while sera interrompue, sinon la boucle while continuera

33
MOHAMED
#include <stdio.h>
main()
{
    char str[100];
    int num;
    while(1) {
        printf("Enter a number: ");
        scanf("%[^0-9]%d",str,&num);
        printf("You entered the number %d\n",num);
    }
    return 0;
}

%[^0-9] dans scanf() engloutit tout ce qui n'est pas entre 0 et 9. Fondamentalement, il nettoie le flux d’entrée de chiffres non numériques et le met dans str. La longueur de la séquence non numérique est limitée à 100. Le %d suivant sélectionne uniquement les entiers dans le flux d'entrée et le place dans num.

2
Swathyprabhu

Vous pouvez créer une fonction qui lit un entier compris entre 1 et 23 ou renvoie 0 s'il n'est pas int

par exemple.

int getInt()
{
  int n = 0;
  char buffer[128];
  fgets(buffer,sizeof(buffer),stdin);
  n = atoi(buffer); 
  return ( n > 23 || n < 1 ) ? 0 : n;
}
1
Anders

Vous devrez répéter votre appel à strtol dans vos boucles pour demander à l'utilisateur de réessayer. En fait, si vous faites de la boucle une do { ... } while(...); au lieu de tout le temps, vous n'obtiendrez pas le même type de comportement. 

Vous devez également formater votre code afin qu’il soit possible de voir où se trouve le code dans une boucle et non. 

0
Mats Petersson

MOHAMED, votre réponse est excellente et m'a vraiment aidé. Ici, j'ai posté un code, que je pense un peu plus simple:

#include <stdio.h>

int getPositive(void);
void clean_input(void);

int main(void) {
     printf("%d\n",getPositive());

     return 0;
}



int getPositive(void) {
    int number;
    char buffer;  // Holds last character from user input. 
                  // (i.e '\n' or any other character besides numbers)
    int flag;     // Holds scanf return value

    do{ 
        flag = scanf("%d%c", &number, &buffer); // Gets input from user

        // While scanf did not read 2 objects (i.e 1 int & 1 char)
        // or the user inputed a number and then a character (eg. 12te)
        // ask user to type a valid value

        while (flag !=2 || buffer!='\n') {

            clean_input();
            printf("%s","You have typed non numeric characters.\n"
                    "Please type an integer\n?");
            flag = scanf("%d%c", &number, &buffer);
        }

        if(number<0) {
            printf("%s","You have typed a non positive integer\n"
                    "Please type a positive integer\n?");

        } else {     // If user typed a non negative value, exit do-while.
              break;
        }

    }while(1);
}

void clean_input(void) {
    while (getchar()!='\n');
    return;
}

Dans mon cas, je veux que le nombre soit simplement positif. Si vous voulez que votre numéro soit compris entre 1 et 23, vous remplacez le number<0 par number<1 || number>23 dans l'instruction if . De plus, vous devrez changer la printf pour imprimer un message approprié. 

0
Elefther
char check1[10], check2[10];
int foo;

do{
  printf(">> ");
  scanf(" %s", check1);
  foo = strtol(check1, NULL, 10); // convert the string to decimal number
  sprintf(check2, "%d", foo); // re-convert "foo" to string for comparison
} while (!(strcmp(check1, check2) == 0 && 0 < foo && foo < 24)); // repeat if the input is not number

Si l'entrée est un nombre, vous pouvez utiliser foo comme entrée.

0
Yonggoo Noh