开发者

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?

  1. it returns NULL when there is an error in input
  2. it returns NULL when it finds an EOF before any "real" characters
  3. it returns the pointer to the buffer
    1. the buffer wasn't completely filled
    2. the buffer was completely filled but there is no more data in input
    3. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜