Самый простой вариант заключается в добавлении ведущее место в строке формата в scanf()
. Например, если вы хотите, чтобы предложить пользователю ввести символ снова при условии плохой вход, вы можете сделать это:
#include <stdio.h>
int main(void)
{
char tmp;
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
while (scanf(" %c", &tmp) != 1 || (tmp != 'p' && tmp != 's')) {
printf("Please enter \"p\" or \"s\": ");
}
printf("You chose %c\n", tmp);
printf("Choose \"a\"scending or \"d\"escending sort: ");
while (scanf(" %c", &tmp) != 1 || (tmp != 'a' && tmp != 'd')) {
printf("Please enter \"a\" or \"d\": ");
}
printf("You chose %c\n", tmp);
return 0;
}
Существует проблема с этим простым подходом, хотя. Если пользователь вводит "aaap"
то полученный результат будет:
Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: aaap
Please enter "p" or "s": Please enter "p" or "s": Please enter "p" or "s": You chose p
Choose "a"scending or "d"escending sort:
Или, что еще хуже, если пользователь вводит правильный первый символ следуют другие символы, такие как "sfffa"
:
Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: sfffa
You chose s
Choose "a"scending or "d"escending sort: Please enter "a" or "d": Please enter "a" or "d": Please enter "a" or "d": You chose a
Потому что scanf()
листья непревзойденные символы во входном потоке, эти символы должны быть обработаны перед чтением входного потока снова. В простом решении, использующем " %c"
, начальное пространство заставляет scanf()
пропускать ведущие символы пробела, но любые другие символы будут подняты. И в реальном мире вы не можете рассчитывать на взаимодействие пользователя, предоставляя корректный ввод.
Одним из типичных портативных решений является использование getchar()
в цикле для очистки входного потока после операции ввода. Вы, возможно, потребуется изменить логику ввода Петля немного, чтобы сделать это:
#include <stdio.h>
...
char tmp;
int c;
int scanf_ret;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
scanf_ret = scanf("%c", &tmp);
while ((c = getchar()) != '\n' && c != EOF) {
continue; // discard extra characters
}
} while (scanf_ret != 1 || (tmp != 'p' && tmp != 's'));
После вызова scanf()
, петля вводится, который считывает и отбрасывает любые символы, оставшиеся во входном потоке. Обратите внимание, что c
является int
и что EOF
проверяется явно в цикле. Функция getchar()
может возвращать EOF
в случае ошибки или если пользователь сигнализирует EOF
с клавиатуры или если вход был перенаправлен из файла. Невозможность проверки на EOF
в таких обстоятельствах приведет к бесконечному циклу.
Это решение работает, даже если пользователь вводит непредсказуемый ввод, так как входной поток всегда очищается после ввода ввода.Обратите внимание, что в строке формата нет необходимости в ведущем пространстве. Другим решением было бы использовать fgets()
для считывания строки ввода в буфер и sscanf()
для разбора буфера.
#include <stdio.h>
#include <stdlib.h>
...
char buffer[1000];
char tmp;
int sscanf_ret;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
exit(EXIT_FAILURE);
}
sscanf_ret = sscanf(buffer, "%c", &tmp);
} while (sscanf_ret != 1 || (tmp != 'p' && tmp != 's'));
fgets()
функция получает целую строку ввода, через символ новой строку, или получает (sizeof buffer) - 1
символов из входного потока, если буфер не является достаточно большим, чтобы вместить все символы во входном потоке и \0
терминатора , Предоставляя буфер большого размера, это должно работать нормально. Пользователь по-прежнему может вводить очень большое количество символов, и, хотя это не будет переполнять буфер, символы будут оставлены во входном потоке для следующей операции чтения. Чтобы избежать таких проблем, , если остались лишние символы, входной поток должен быть очищен после вызова fgets()
с использованием петли getchar()
сверху.
Все эти методы имеют свое место. Очевидно, что последние два гораздо более надежны, чем первые, и, поскольку маловероятно, что пользователь вводит 999 символов в приглашении ввода (включая новую строку), они почти равны. Но, во избежание всякого удивления, явное очищение входного потока после получения пользовательского ввода является лучшим.
Проводили ли вы базовую отладку? Как использовать отладчик и/или отладочные операторы печати, чтобы узнать, какие значения принимает 'tmp', когда он работает, а когда нет? – kaylum
Он отлично работает в моей системе. – Jarvis
Что вы читали перед этим? Оставило ли он новую строку на входе? Вы должны рассмотреть возможность использования 'if (scanf ("% c ", & tmp)! = 1) {... handle error ...}' где пробел перед '% c' не является случайностью - он пропускает пробел (пробелы, вкладки, newlines) и ждет чего-то другого ('' '' '' '' '' '' '), который будет введен. –