2017-01-29 15 views
2

Я хочу, чтобы пользователь вводил только два целых числа и не более двух или менее двух. Кроме того, после недопустимого ввода, я хочу напечатать сообщение об ошибке и попросить пользователя снова ввести два целых числа. Пользователь должен ввести два целых числа, разделенных пробелом, а не новую строку. Так, например:
1) Действительный вход: 1 2
2) Неправильный ввод: 1
3) Неправильный ввод: 1 2 3Как предотвратить ввод пользователем более или менее ввода, чем требуется в C?

Я попытался его с двух следующих подходов:

#include<stdio.h> 

int main(){ 

    int first; 
    int second; 
    printf("Enter input:\n"); 
    int returnValue = scanf("%d %d", &first, &second); 
    while(returnValue != 2){ 
     printf("Invalid input. Please enter again: \n"); 
     returnValue = scanf("%d %d", &first, &second); 
    } 
    printf("First: %d Second: %d\n", first, second); 
    return 0; 
} 

В этом первом подходе, включающем scanf, я не могу запретить пользователю вводить каждое целое число в строке новой строки. Я также не могу ограничить ввод только двумя номерами. То есть, если пользователь вводит более 2 целых чисел, тогда программа принимает первые 2 целых числа и игнорирует третью. Я хочу напечатать ошибку в этом случае.

Мой другой подход предполагает fgets и sscanf:

#include<stdio.h> 

int main(){ 

    int first; 
    int second; 
    printf("Enter input:\n"); 
    char line[20]; 
    fgets(line, sizeof(line), stdin); 
    int returnValue = sscanf(line, "%d %d", &first, &second); 

    while(returnValue != 2){ 
    printf("Invalid input. Please enter again: \n"); 
    fgets(line, sizeof(line), stdin); 
    returnValue = sscanf(line, "%d %d", &first, &second); 
    } 

    printf("First: %d Second: %d\n", first, second); 
    return 0; 
} 

В этом подходе, я могу напечатать сообщение об ошибке, если пользователь нажимает клавишу ВВОД после ввода только один целое. Но я не могу ограничить ввод только двумя номерами. То есть, если пользователь вводит более 2 целых чисел, тогда программа принимает первые 2 целых числа и игнорирует третью. Я хочу напечатать ошибку в этом случае.

Итак, мой вопрос в том, что мои требования достижимы путем изменения первого подхода, а также второго подхода?

Спасибо.

+2

Без аппаратной поддержки (например, клавиатура со всеми отключенными буквами, какой-либо метод для электрокодирования пользователя, если они попали в букву А и т. Д.), Невозможно ПРЕДОТВРАТИТЬ пользователя от ввода любого вводимого им ввода. Самое большее, что вы можете сделать, это принять вход и проверить его. Если встречается недопустимый вход, некоторые варианты - игнорировать неисправные части, игнорировать некоторый блок данных (например, целую строку, если он содержит какой-либо плохой ввод), или просто отказаться от ввода и продолжать просить, пока пользователь не введет что-то действительное. – Peter

+1

«Итак, мой вопрос в том, что мои требования достижимы путем изменения первого подхода, а также второго подхода?» -> Да, [пример] (http://stackoverflow.com/a/27805565/2410359), но не стоит. Пойдите с [@David Bowling] (http://stackoverflow.com/a/41917246/2410359) – chux

ответ

3

Одним решением было бы используйте преобразование %n после двух преобразований %d. Спецификация преобразования %n не соответствует никаким символам, но сохраняет количество символов, считанных с этой точки в строке формата. Так, при вызове:

sscanf(line, "%d %d %n", &first, &second, &bufPos); 

если второй %d достигается, то bufPos будет удерживать индекс символа после последнего символа чтения в line. Поскольку перед %n есть пробел, будут считаны и пропущены ноль или более символов пробела, прежде чем значение индекса будет сохранено в bufPos. Таким образом, после действительной записи bufPos будет указывать терминатор \0. Если какой-либо другой символ найден в line при этом индексе, на входе присутствовали посторонние символы.

Это измененная версия вашего второго примера кода. После fgets() считывает строку ввода, sscanf() используется для сканирования строки. Если сделано менее 2 совпадений, или если line[bufPos] не '\0', то badInput установлено на true.Входной цикл представляет собой цикл do, который выполняется один раз и продолжает выполнять, пока badInput равен true.

#include <stdio.h> 
#include <stdlib.h>     // for exit() 
#include <stdbool.h>    // for bool type 

#define BUF_SIZE 100 

int main(void) 
{ 
    int first; 
    int second; 
    char line[BUF_SIZE]; 
    int returnValue; 
    int bufPos; 
    bool badInput = false; 

    do { 
     if (badInput) { 
      printf("Invalid input. Please enter again: "); 
      badInput = false; 
     } else { 
      printf("Enter input: "); 
     } 
     if (fgets(line, sizeof(line), stdin) == NULL) { 
      perror("Error in fgets()"); 
      exit(EXIT_FAILURE); 
     } 

     returnValue = sscanf(line, "%d %d %n", &first, &second, &bufPos); 

     if (returnValue < 2 || line[bufPos] != '\0') { 
      badInput = true; 
     } 

    } while (badInput); 

    printf("First: %d Second: %d\n", first, second); 

    return 0; 
} 

Пример взаимодействия:

Enter input: 1 
Invalid input. Please enter again: 1 2 3 
Invalid input. Please enter again: 
Invalid input. Please enter again: 1 2 
First: 1 Second: 2 
+0

Это сработало. Благодаря! –

1

Для предотвращения проблем при запросе char * вы можете использовать regular expression. Если вы не обязаны получить два в одном scanf вы могли бы использовать эту функцию:

int secure_input(int max, int min) { 
    int choice,buffer; 
    do { 
     choice = -1;//initialize in a values not included among min and max 
     scanf("%d", &choice); 
     while ((buffer = getchar()) != '\n' ? buffer != EOF : false); // empty the buffer to avoid infinite loop 
    } while (choice > max ? true : choice < min); 
    return choice; 
} 

В вашей основной функции вы просто вызвать функцию так:

first = secure_input(2;1); 
+0

При редкой ошибке ввода значение 'choice' может или не может быть' -1'. Лучше проверить возвращаемое значение 'scanf()'. Приведение к 'char' в' buffer = (char) getchar() 'приводит к бесконечному циклу, когда происходит конец файла, а' char' - без знака. Лучше всего использовать 'int buffer' и отказаться от кастинга. – chux

0

В отличие от других ответов, вы можете также разобрать вход, используя strtok(), а затем проверить, как было обнаружено много номеров. Этот подход сложный, но он предлагает другой взгляд на проблему.

Внутри вашего while() петли, вы можете проверить, сколько расставленные числа были найдены fgets(), а затем, если были найдены только 2, то вы можете break из петли. В противном случае продолжайте поиск. Выйдя из цикла, вы можете просто sscanf() два целых числа от самого последнего ввода. Вы также можете использовать strtol(), чтобы проверить, действительны ли целые числа.

Примечание:strtok() является reeantrant, и он изменяет строку, которую он анализирует. Поэтому в этом случае вам может понадобиться создать его копию. Вы можете использовать strdup() или malloc() для этого.

Вот пример кода, который показывает это:

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

#define LINESIZE 20 
#define BASE 10 

int main(void) { 
    char line[LINESIZE]; 
    const int n = LINESIZE; 

    char *number, *copy, *endptr; 
    const char *delim = " "; 

    int first, second, check, invalidnum; 
    size_t slen, count; 

    while (1) { 
     printf("Enter input: "); 
     if (fgets(line, n, stdin) == NULL) { 
      printf("Error reading buffer from fgets()\n"); 
      exit(EXIT_FAILURE); 
     } 

     slen = strlen(line); 
     if (slen > 0 && line[slen-1] == '\n') { 
      line[slen-1] = '\0'; 
     } else { 
      printf("Buffer overflow detected\n"); 
      exit(EXIT_FAILURE); 
     } 

     copy = strdup(line); 

     count = 0; 
     invalidnum = 0; 
     number = strtok(copy, delim); 
     while (number != NULL) { 
      check = strtol(number, &endptr, BASE); 
      if (endptr == number || check == 0) { 
       invalidnum = 1; 
       break; 
      } 
      count++; 
      number = strtok(NULL, delim); 
     } 
     free(copy); 
     copy = NULL; 

     if (count != 2 || invalidnum) { 
      printf("Invalid input\n\n"); 
     } else { 
      break; 
     } 
    } 

    if (sscanf(line, "%d %d", &first, &second) != 2) { 
     printf("Unexpected error from sscanf()\n"); 
     exit(EXIT_FAILURE); 
    } 
    printf("first = %d, second = %d\n", first, second); 

    return 0; 
} 

Это просто другой подход к вашей проблеме. С точки зрения простоты, @David Bowling имеет лучшую идею, и я бы предложил использовать его.