Как сканировать только одно целое и повторять чтение, если пользователь вводит нецифровые символы?

Вот небольшая проблема с tyro с кодом C, пытающимся просто запретить пользователю вводить символ или целое число меньше 0 или более 23.

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

int main(void)
{
    const char *input;
    char *iPtr;
    int count = 0;
    int rows;

    printf("Enter an integer: ");
    scanf("%s", input);
    rows = strtol(input, &iPtr, 0);
    while( *iPtr != '\0') // Check if any character has been inserted
    {
        printf("Enter an integer between 1 and 23: ");
        scanf("%s", input);
    }
    while(0 < rows && rows < 24) // check if the user input is within the boundaries
    {
        printf("Select an integer from 1 to 23: ");
        scanf("%s", input);
    }  
    while (count != rows)  
    {  
        /* Do some stuff */  
    }  
    return 0;  
}

Я сделал это на полпути, и небольшое повышение будет оценено.

Ответ 1

Используйте scanf("%d",&rows) вместо scanf("%s",input)

Это позволяет получить прямое значение integer из stdin без необходимости преобразования в int.

Если пользователь вводит строку, содержащую нецифровые символы, вы должны очистить свой stdin до следующего scanf("%d",&rows).

ваш код может выглядеть так:

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

int clean_stdin()
{
    while (getchar()!='\n');
    return 1;
}

int main(void)  
{ 
    int rows =0;  
    char c;
    do
    {  
        printf("\nEnter an integer from 1 to 23: ");

    } while (((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) || rows<1 || rows>23);

    return 0;  
}

Объяснение

1)

scanf("%d%c", &rows, &c)

Это означает, что от пользователя вводится целое число и рядом с ним нецифровой символ.

Пример1: Если пользователь вводит aaddk, а затем ENTER, scanf вернет 0. Ничего не закрыто

Пример2: Если пользователь вводит 45, а затем ENTER, scanf вернет 2 (2 элемента закрыты). Здесь %d является capting 45 и %c является capting \n

Пример3: Если пользователь вводит 45aaadd, а затем ENTER, scanf вернет 2 (2 элемента закрыты). Здесь %d - capting 45, а %c - capting a

2)

(scanf("%d%c", &rows, &c)!=2 || c!='\n')

В примере1: это условие TRUE, потому что scanf return 0 (!=2)

В примере2: это условие FALSE, потому что scanf return 2 и c == '\n'

В примере3: это условие TRUE, потому что scanf return 2 и c == 'a' (!='\n')

3)

((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin())

clean_stdin() всегда TRUE, потому что функция всегда возвращает 1

В примере 1: (scanf("%d%c", &rows, &c)!=2 || c!='\n') - TRUE, поэтому условие после && должно быть проверено, чтобы выполнить clean_stdin(), и все условие TRUE

В примере2: (scanf("%d%c", &rows, &c)!=2 || c!='\n') есть FALSE, поэтому условие после && не будет проверено (поскольку его результат будет равен всего FALSE), поэтому clean_stdin() не будет выполняться, и все условие FALSE

В примере 3: (scanf("%d%c", &rows, &c)!=2 || c!='\n') - TRUE, поэтому условие после && должно быть проверено, чтобы выполнить clean_stdin() и все условие TRUE

Итак, вы можете заметить, что clean_stdin() будет выполняться только в том случае, если пользователь вводит строку, содержащую нецифровой символ.

И это условие ((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) вернет FALSE, только если пользователь вводит integer и ничего больше

И если условие ((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) равно FALSE и integer находится между и 1 и 23, тогда цикл while будет ломаться иначе, цикл while будет продолжен

Ответ 2

#include <stdio.h>
main()
{
    char str[100];
    int num;
    while(1) {
        printf("Enter a number: ");
        scanf("%[^0-9]%d",str,&num);
        printf("You entered the number %d\n",num);
    }
    return 0;
}

%[^0-9] в scanf() копирует все, что не находится между 0 и 9. По сути, он очищает входной поток без цифр и помещает его в str. Ну, длина нецифровой последовательности ограничена 100. Следующие %d выбирают только целые числа во входном потоке и помещают его в num.

Ответ 3

Вы можете создать функцию, которая читает целое число от 1 до 23 или возвращает 0, если не-int

например.

int getInt()
{
  int n = 0;
  char buffer[128];
  fgets(buffer,sizeof(buffer),stdin);
  n = atoi(buffer); 
  return ( n > 23 || n < 1 ) ? 0 : n;
}

Ответ 4

char check1[10], check2[10];
int foo;

do{
  printf(">> ");
  scanf(" %s", check1);
  foo = strtol(check1, NULL, 10); // convert the string to decimal number
  sprintf(check2, "%d", foo); // re-convert "foo" to string for comparison
} while (!(strcmp(check1, check2) == 0 && 0 < foo && foo < 24)); // repeat if the input is not number

Если ввод является номером, вы можете использовать foo в качестве вашего ввода.

Ответ 5

Вам нужно будет повторить свой вызов strtol внутри ваших петель, где вы просите пользователя повторить попытку. Фактически, если вы сделаете цикл a do { ... } while(...);, а не время, вы не получите одинаковое повторение в два раза.

Вы также должны форматировать свой код, чтобы можно было видеть, где код находится внутри цикла, а не.

Ответ 6

MOHAMED, ваш ответ велик, и это действительно помогло мне. Здесь я разместил код, который, я думаю, немного проще:

#include <stdio.h>

int getPositive(void);
void clean_input(void);

int main(void) {
     printf("%d\n",getPositive());

     return 0;
}



int getPositive(void) {
    int number;
    char buffer;  // Holds last character from user input. 
                  // (i.e '\n' or any other character besides numbers)
    int flag;     // Holds scanf return value

    do{ 
        flag = scanf("%d%c", &number, &buffer); // Gets input from user

        // While scanf did not read 2 objects (i.e 1 int & 1 char)
        // or the user inputed a number and then a character (eg. 12te)
        // ask user to type a valid value

        while (flag !=2 || buffer!='\n') {

            clean_input();
            printf("%s","You have typed non numeric characters.\n"
                    "Please type an integer\n?");
            flag = scanf("%d%c", &number, &buffer);
        }

        if(number<0) {
            printf("%s","You have typed a non positive integer\n"
                    "Please type a positive integer\n?");

        } else {     // If user typed a non negative value, exit do-while.
              break;
        }

    }while(1);
}

void clean_input(void) {
    while (getchar()!='\n');
    return;
}

В моем случае я хочу, чтобы номер был просто положительным. Если вы хотите, чтобы ваш номер находился между 1 и 23, вы заменяете number<0 на number<1 || number>23 в инструкции if. Также вам нужно будет изменить printf, чтобы напечатать соответствующее сообщение.

Ответ 7

Приведенный выше сценарий, созданный Yonggoo Noh, хорошо сработал для меня.
Я изменил его, чтобы включить значение 0, чтобы вернуться к main() в любое время, но у меня есть проблема:
- Иногда, после возврата в main(), когда я снова вхожу в скрипт, он запрашивает у меня значение ("check1") 2 или более раз.
Почему это произойдет?

char check1[10], check2[10];
int foo;

do{
  printf(">> ");
  scanf(" %s", check1);
  foo = strtol(check1, NULL, 10); // convert the string to decimal number
  sprintf(check2, "%d", foo); // re-convert "foo" to string for comparison
} while (!(strcmp(check1, check2) == 0 && ((0 < foo && foo < 24) || foo==0))); // repeat if the input is not number

if (n==0){ main(); }