Программа приходит в бешенство после плохого ввода (C) - PullRequest
0 голосов
/ 01 февраля 2019

У меня есть функция, которая должна получить число от stdin.Он должен проверять, является ли это действительное число, и при желании установить его в определенном диапазоне.Если ввод достаточно длинный (скажем, 10 символов), то функция печатает сообщение об ошибке и сбрасывает цикл, все работает как задумано.Однако если я введу что-то нелепо длинное, например:

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

, тогда что-то пойдет не такЦикл продолжает сбрасываться, но независимо от того, что я набираю, он больше не принимает его, даже если он будет полностью действительным.

Полагаю, это может быть из-за переполнения stdin?Но не в этом ли смысл fgets() - он читает только определенное количество символов и отбрасывает все остальное?Как обойти это без использования исключений?

Рассматриваемая функция:

int safeinp(int * num, const char *message, int low, int high)
{
    long a;
    char buf[11]; // 9 digits for the number + "\n\0"
    int success; // flag for successful conversion

    do
    {
        puts(message);
        if (!fgets(buf, 11, stdin))
        {
            fprintf(stderr, "Unagle to obtain input.\n");
            return 1;
        }

        // have some input, convert it to integer:
        char *endptr;

        a = strtol(buf, &endptr, 12);
        if (errno == ERANGE)
        {
//this if() right here is what gets executed endlessly if the input is bad
                fprintf(stderr, "Invalid number.\n");
                success = 0;
            }
            else if (endptr == buf)
            {
                fprintf(stderr, "Invalid input.\n");
                success = 0;
            }
            else if (*endptr && *endptr != '\n')
            {
                fprintf(stderr, "Conversion error.\n");
                success = 0;
            }
            else
            {
                success = 1;
                if (low != high) {
                    a = (a < low) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), low : a;
                    a = (a > high) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), high : a;
                }
                *num = a;
            }
        } while (!success); 
        return success;
    }

Проблема возникает в Visual Studio 2017 для Windows 10.

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Вам необходимо проверить a после возврата из strtol для LONG_MIN или LONG_MAX.Не гарантируется, что errno не будет установлен на ERANGE после правильного синтаксического анализа ввода.

       The  strtol() function returns the result of the conversion, unless the
       value would underflow or overflow.  If an  underflow  occurs,  strtol()
       returns  LONG_MIN.   If  an overflow occurs, strtol() returns LONG_MAX.
       In both cases, errno is set to ERANGE.

Функции libc не сбрасываются errno при успехе!

Кроме того, ваш вход strtol(..,.., base) -> ваша база 12?

0 голосов
/ 01 февраля 2019

Нет, fgets() не не очищает «буфер».Если вы введете длинную строку, ваш код будет читать по 10 символов за раз, пока он не достигнет конца строки ввода.Следующий цикл будет ждать снова.

Пример в разобранном виде:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  char buf[11];

  do {
    puts("TEST");
    fflush(stdout);
    if (!fgets(buf, sizeof(buf), stdin)) {
      fprintf(stderr, "Unagle to obtain input.\n");
      return 1;
    }

    printf("input: %s\n", buf);
  } while (1);

  return(0);
}

Тестовый прогон:

$ gcc -Wall -o dummy dummy.c
$ ./dummy 
TEST
123456789012345678901234567890
input: 1234567890
TEST
input: 1234567890
TEST
input: 1234567890
TEST
input: 

TEST
^C
$

ОБНОВЛЕНИЕ: предложение, которое пытаетсяесть все оставшиеся символы до новой строки:

    /* replacement for fgets(buf, sizeof(buf), stdin) */
    char *p = buf;
    char c;
    unsigned left = sizeof(buf) - 1;
    while ((left-- > 0) && ((c = fgetc(stdin)) != '\n')) {
      if (feof(stdin)) {
        return(1);
      }
      *p++ = c;
    }
    *p++ = '\0';

    /* eat the rest until newline */
    while (c != '\n') {
      c = fgetc(stdin);
      if (feof(stdin)) {
        return(1);
      }
    }

Новый тестовый запуск:

$ gcc -Wall -o dummy dummy.c
$ ./dummy 
TEST
123456789012345678901234567890
input: 1234567890
TEST
1
input: 1
TEST
1234567890
input: 1234567890
TEST
^C
...