Как запретить пользователю вводить больше данных, чем максимальный лимит? - PullRequest
4 голосов
/ 14 ноября 2010

Этот код запрашивает у пользователя данные, а затем число:

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

Проблема в том, что кроме инструкции, в которой указано максимальное количество символов, нет ничего, что мешало бы пользователю ввести больше, который впоследствии читается в num как мусор:

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

Есть ли способ отбросить все, что есть на STDIN после вызова fgets?

Ответы [ 4 ]

5 голосов
/ 14 ноября 2010

Насколько мне известно, единственным переносимым решением является исчерпание буфера самостоятельно:

while (getchar() != EOF);

Обратите внимание, что fflush(stdin); - это , а не ответ .

РЕДАКТИРОВАТЬ: Если вы хотите сбросить только символы до следующей новой строки, вы можете сделать:

int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
5 голосов
/ 14 ноября 2010

Функция scanf () ужасна для пользовательского ввода, и она не так уж хороша для ввода файла, если вы не уверены, что ваши входные данные верны (не доверяйте!) Кроме того, вы всегда должны проверять возвращаемое значение дляfgets (), поскольку NULL указывает EOF или другое исключение.Имейте в виду, что вы получаете символ новой строки пользователя в конце ваших данных fgets (), если максимум не достигнут первым.Я мог бы сделать это следующим образом:

#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;
}
1 голос
/ 14 ноября 2010

Что "может произойти" с fgets?

  1. , возвращается NULL при ошибке ввода
  2. , возвращается NULL, когда находит EOF перед любыми "настоящими" символами
  3. возвращает указатель на буфер
    1. буфер не был полностью заполнен
    2. буфер был полностью заполнен, но больше нетданные на входе
    3. буфер полностью заполнен и на входе больше данных

Как можно различить 1 и 2?
с feof

Как можно различить 3.1., 3.2. и 3.3.
Определяя, где были записаны завершающий нулевой байт и разрыв строки:
Если выводбуфер имеет значение '\n', тогда больше нет данных (возможно, буфер был полностью заполнен)
Если нет '\n' AND , то '\0' находится в последней позиции буфератогда вы знаете, что ожидается больше данных;если '\0' находится перед последней позицией буфера, вы нажали EOF в потоке, который не заканчивается разрывом строки.

вот так

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

Обычные, неполные тесты: buf[buflen - 1] == '\n' и проверка fgets возвращаемого значения ...

while (fgets(buf, sizeof buf, stdin)) {
    if (buf[strlen(buf) - 1] != '\n') /* deal with extra input */;
}
0 голосов
/ 14 ноября 2010

Я бы прочитал данные и затем проверил их на наличие ошибок пользователя:

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

С другой стороны, если вы не хотите этого делать, просто прочитайте num дважды и откажитесь от первого.

...