How to prevent the user from entering more data than the maximum limit?
This code asks the user for data and subsequently 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(开发者_如何学JAVA"%d", &num);
printf("data: %s", c);
printf("num: %d\n", num);
}
$
The problem is that apart from the instruction that states the maximum number of chars, there is nothing that stops the user from entering more, which is subsequently read into num
as junk:
$ ./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 discard all that is there on STDIN
after the fgets
call?
The scanf() function is terrible for user input, and it's not that great for file input unless you somehow know your input data is correct (don't be that trusting!) Plus, you should always check the return value for fgets() since NULL indicates EOF or some other exception. Keep in mind that you get the user's newline character at the end of your fgets() data unless the maximum is reached first. I might do it this way as a 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;
}
To my knowledge, the only portable solution is to exhaust the buffer yourself:
while (getchar() != EOF);
Note that fflush(stdin);
is not the answer.
EDIT: If you only want to discard characters until the next newline, you can do:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
What "can happen" to fgets
?
- it returns
NULL
when there is an error in input - it returns
NULL
when it finds anEOF
before any "real" characters - it returns the pointer to the buffer
- the buffer wasn't completely filled
- the buffer was completely filled but there is no more data in input
- the buffer was completely filled and there is more data in input
How can you distinguish between 1
and 2
?
with feof
How can you distinguish between 3.1.
, 3.2.
and 3.3.
By determining where the terminating null byte and line break were written:
If the output buffer has a '\n'
then there is no more data (the buffer may have been completely filled)
If there is no '\n'
AND the '\0'
is at the last position of the buffer, then you know there is more data waiting; if the '\0'
is before the last position of the buffer, you've hit EOF
in a stream that doesn't 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.)");
}
}
The usual, incomplete tests, are buf[buflen - 1] == '\n'
and checking fgets
return value ...
while (fgets(buf, sizeof buf, stdin)) {
if (buf[strlen(buf) - 1] != '\n') /* deal with extra input */;
}
I would read the data and then check it for 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 don't want to do this, just read num twice and discard the first one.
精彩评论