How to prevent a user from entering more data than the maximum limit?

This code asks the user for data, and then a number:

$ 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); } $ 

The problem is that in addition to the instruction that indicates the maximum number of characters, nothing prevents the user from entering more, which is subsequently read into num as garbage:

 $ ./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 $ 

Is there a way to drop everything on STDIN after calling fgets ?

+4
source share
4 answers

The scanf () function is terrible for user input, and it is not so good for file input if you somehow do not know that your input is correct (do not rely on this!) In addition, you should always check the return value for fgets (), since NULL indicates EOF or some other exception. Keep in mind that you will get a newline character at the end of your fgets () data if the maximum is not reached first. I could do it like the first pass:

 #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; } 
+5
source

As far as I know, the only portable solution is to exhaust the buffer itself:

 while (getchar() != EOF); 

Note that fflush(stdin); not an answer .

EDIT: If you only want to drop characters to the next new line, you can do:

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

What can happen with fgets ?

  • it returns NULL when there is an input error
  • it returns NULL when it finds EOF before any "real" characters
  • returns a pointer to a buffer
    • buffer was not full
    • the buffer was full, but there is no more data in the input
    • the buffer was full and more data was input

How can you distinguish between 1 and 2 ?
with feof

How can you distinguish 3.1. , 3.2. and 3.3.
Determining where the final null byte and line break were written:
If the output buffer has '\n' , then there is no more data (the buffer can be full)
If there is no '\n' AND , then '\0' is in the last position of the buffer, then you know that more data was expected; if '\0' is before the last position of the buffer, you will press EOF in the stream, which does not end with a line break.

like this

 /* 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.)"); } } 

Common, incomplete tests: buf[buflen - 1] == '\n' and checking the return value of fgets ...

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

I would read the data and then check it for a user error:

 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! "); } 

On the other hand, if you do not want to do this, just double-click num and drop the first one.

-1
source

All Articles