2010-11-14 55 views
4

Ce code demande à l'utilisateur des données et par la suite un numéro:Comment empêcher l'utilisateur d'entrer plus de données que la limite maximale?

$ cat read.c 
#include<stdio.h> 
#include<stdlib.h> 
#define MAX 10 

int main() { 
    char* c = (char*) malloc(MAX * sizeof(char)); 
    int num; 

    printf("Enter data (max: %d chars):\n", MAX); 
    fgets(c, MAX, stdin); 
    // how do I discard all that is there on STDIN here? 

    printf("Enter num:\n"); 
    scanf("%d", &num); 

    printf("data: %s", c); 
    printf("num: %d\n", num); 
} 
$ 

Le problème est que, en dehors de l'instruction qui indique le nombre maximum de caractères, il n'y a rien qui empêche l'utilisateur d'entrer plus, ce qui est ensuite lu dans num comme indésirable:

$ ./read 
Enter data (max 10 chars): 
lazer 
Enter num: 
5 
data: lazer 
num: 5 
$ ./read 
Enter data (max 10 chars): 
lazerprofile 
Enter num: 
data: lazerprofnum: 134514043 
$ 

est-il un moyen de rejeter tout ce qui est là STDIN après l'appel fgets?

Répondre

5

La fonction scanf() est terrible pour l'entrée de l'utilisateur, et ce n'est pas grande pour l'entrée de fichier à moins que vous ne sachiez en quelque sorte que vos données d'entrée sont correctes (ne faites pas confiance!) De plus, vous devriez toujours vérifier la valeur de retour de fgets() puisque NULL indique EOF ou une autre exception. Gardez à l'esprit que vous obtenez le caractère de retour à la ligne de l'utilisateur à la fin de vos données fgets() à moins que le maximum ne soit atteint en premier. Je pourrais le faire de cette façon comme une première passe:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define MAX 10 

void eat_extra(void) { 
    int ch; 

    // Eat characters until we get the newline 
    while ((ch = getchar()) != '\n') { 
     if (ch < 0) 
      exit(EXIT_FAILURE); // EOF! 
    } 
} 

int main() { 
    char c[MAX+1]; // The +1 is for the null terminator 
    char n[16]; // Arbitrary maximum number length is 15 plus null terminator 
    int num; 

    printf("Enter data (max: %d chars):\n", MAX); 
    if (fgets(c, MAX, stdin)) { // Only proceed if we actually got input 
     // Did we get the newline? 
     if (NULL == strchr(c, '\n')) 
      eat_extra(); // You could just exit with "Too much data!" here too 

     printf("Enter num:\n"); 
     if (fgets(n, sizeof(n) - 1, stdin)) { 
      num = atoi(n); // You could also use sscanf() here 
      printf("data: %s", c); 
      printf("num: %d\n", num); 
     } 
    } 

    return 0; 
} 
+0

Triste mais vrai. Modifié! – spstanley

+0

C'est mieux. '-1' enlevé – pmg

+0

il devrait être' fgets (c, MAX + 1, stdin) 'pas' MAX'. et 'fgets (n, sizeof (n), stdin)' au lieu de 'sizeof (n) - 1'. lisez le manuel de 'fgets' – user102008

-1

je lirais les données et vérifier l'erreur de l'utilisateur:

bool var = true; 
while var { 
printf("Enter data (max: %d chars):\n", MAX); 
fgets(c, MAX, stdin); 
// how do I discard all that is there on STDIN here? 
if(strlen(c) <= 10) 
var = false; 
else 
printf("Too long, try again! "); 
} 

D'autre part, si vous ne voulez pas cela, il suffit de lire deux fois num et jeter le premier.

+0

-1 Après 'fgets (c, MAX, stdin)', 'strlen (c)' est ** TOUJOURS ** inférieur à MAX. – pmg

5

A ma connaissance, la seule solution portable est d'épuiser le tampon vous:

while (getchar() != EOF); 

Notez que fflush(stdin); est not the answer.

EDIT: Si vous voulez seulement jeter des caractères jusqu'à la prochaine nouvelle ligne, vous pouvez le faire:

int ch; 
while ((ch = getchar()) != '\n' && ch != EOF); 
+0

Absolument aimer votre réponse. Mais j'ai un problème où les fgets m'obligent à appuyer deux fois sur des chaînes extrêmement volumineuses. Il reste là à attendre une autre presse. Des idées pourquoi? Merci encore –

1

Qu'est-ce que « peut arriver » à fgets?

  1. il retourne NULL lorsqu'il y a une erreur dans l'entrée
  2. elle retourne NULL lorsqu'il trouve un EOF avant les caractères « vrais »
  3. il renvoie le pointeur vers le tampon
    1. le tampon wasn
    2. le tampon a été complètement rempli mais il n'y a plus de données dans l'entrée
    3. le tampon a été complètement rempli un nd il y a plus de données en entrée

Comment pouvez-vous distinguer entre 1 et 2?
avec feof

Comment pouvez-vous distinguer entre 3.1., 3.2. et 3.3.
En déterminant où l'octet nul et saut de ligne ont été écrites:
Si le tampon de sortie a un '\n' alors il n'y a pas d'autres données (la tampon peut avoir été complètement rempli)
S'il n'y a pas '\n'et le '\0' est à la dernière position du tampon, alors vous savez qu'il y a plus de données en attente; Si le '\0' est avant la dernière position du tampon, vous avez tapé EOF dans un flux qui ne se termine pas par un saut de ligne.

comme ce

/* fgets fun */ 
/* 
char buf[SOMEVALUE_LARGERTHAN_1]; 
size_t buflen; 
*/ 
if (fgets(buf, sizeof buf, stdin)) { 
    buflen = strlen(buf); 
    if (buflen) { 
     if (buf[buflen - 1] == '\n') { 
      puts("no more data (3.1. or 3.2.)"); /* normal situation */ 
     } else { 
      if (buflen + 1 == sizeof buf) { 
       puts("more data waiting (3.3.)"); /* long input line */ 
      } else { 
       puts("EOF reached before line break (3.1.)"); /* shouldn't happen */ 
      } 
     } 
    } else { 
     puts("EOF reached before line break (3.1.)"); /* shouldn't happen */ 
    } 
} else { 
    if (feof(stdin)) { 
     puts("EOF reached (2.)"); /* normal situation */ 
    } else { 
     puts("error in input (1.)"); 
    } 
} 

Les habituels, les tests incomplets, sont buf[buflen - 1] == '\n' et vérifier la valeur de retour fgets ...

while (fgets(buf, sizeof buf, stdin)) { 
    if (buf[strlen(buf) - 1] != '\n') /* deal with extra input */; 
}