Scanf пропускает каждый цикл while в C
Я пытаюсь разработать простую текстовую игру палача, и основной игровой цикл начинается с приглашения ввести догадку по каждой букве, а затем проверяет, находится ли буква в слове, и лишает его жизни, если она нет. Однако, когда я запускаю игру, подсказка появляется дважды каждый раз, и программа не ждет ввода пользователя. Это также отнимает жизнь (одну жизнь, если это был правильный ввод, две, если это не так), поэтому все, что он принимает, не совпадает с предыдущим. Вот мой игровой цикл, немного упрощенный:
while (!finished)
{
printf("Guess the word '%s'\n",covered);
scanf("%c", ¤tGuess);
i=0;
while (i<=wordLength)
{
if (i == wordLength)
{
--numLives;
printf("Number of lives: %i\n", numLives);
break;
} else if (currentGuess == secretWord[i]) {
covered[i] = secretWord[i];
secretWord[i] = '*';
break;
}
++i;
}
j=0;
while (j<=wordLength)
{
if (j == (wordLength)) {
finished = 1;
printf("Congratulations! You guessed the word!\n");
break;
} else {
if (covered[j] == '-') {
break;
}
}
++j;
if (numLives == 0) {
finished = 1;
}
}
}
Я предполагаю, что проблема в том, что Сканф думает, что он принял что-то, когда это не так, но я не знаю почему. У кого-нибудь есть идеи? Я использую gcc 4.0.1 на Mac OS X 10.5.
10 ответов
Когда вы читаете ввод с клавиатуры с scanf()
, ввод читается после нажатия клавиши ввода, но новая строка, сгенерированная клавишей ввода, не используется вызовом scanf()
, Это означает, что в следующий раз, когда вы будете читать из стандартного ввода, вас будет ждать новая строка (которая сделает следующее scanf()
звонок мгновенно без данных).
Чтобы избежать этого, вы можете изменить свой код на что-то вроде:
scanf("%c%*c", ¤tGuess);
%*c
соответствует одному символу, но звездочка указывает, что символ не будет нигде сохранен. Это имеет эффект потребления символа новой строки, сгенерированного клавишей ввода, чтобы при следующем вызове scanf()
вы начинаете с пустого входного буфера.
Предостережение: если пользователь нажимает две клавиши, а затем нажимает ввод, scanf()
вернет первое нажатие клавиши, съест второе и оставит перевод строки для следующего входного вызова. Причуды как это - одна из причин, почему scanf()
и друзей избегают многие программисты.
Newlines.
Первый раз в цикле scanf() читает символ. Тогда это читает новую строку. Затем он читает следующий символ; повторение.
Как исправить?
Я редко использую scanf(), но если вы используете строку формата "%.1s"
, он должен пропустить пробел (включая символы новой строки), а затем прочитать символ, не являющийся пробелом. Тем не менее, он будет ожидать массив символов, а не один символ:
char ibuff[2];
while ((scanf("%.1s", ibuff) == 1)
{
...
}
Разбейте проблему на более мелкие части:
int main(void) {
char val;
while (1) {
printf("enter val: ");
scanf("%c", &val);
printf("got: %d\n", val);
}
}
Вывод здесь:
enter val: g
got: 103
enter val: got: 10
Почему бы scanf
дать вам еще 10?
Поскольку мы напечатали номер ASCII для нашего значения, "10" в ASCII - это "ввод", поэтому scanf
также необходимо захватить клавишу ввода как символ.
Конечно же, глядя на ваш scanf
строка, вы просите один символ каждый раз в вашем цикле. Управляющие символы также считаются символами и будут подобраны. Например, вы можете нажать "esc", затем "enter" в вышеприведенном цикле и получить:
enter val: ^[
got: 27
enter val: got: 10
Просто предположение, но вы вводите один символ с помощью scanf, но пользователь должен ввести предположение плюс символ новой строки, который используется как отдельный символ предположения.
scanf(" %c", &fooBar);
Обратите внимание на пространство перед %c
, Это важно, потому что оно соответствует всем предшествующим пробелам.
Джим и Джонатан понимают это правильно.
Чтобы заставить строку scanf делать то, что вы хотите (использовать символ новой строки без помещения его в буфер), я бы изменил его на
scanf("%c\n", ¤tGuess);
(Обратите внимание \n
)
Обработка ошибок на этом, однако, ужасна. По крайней мере, вы должны проверить возвращаемое значение из scanf против 1 и игнорировать ввод (с предупреждением), если он не возвращает это.
Пара замечаний, которые я заметил:
scanf("%c")
прочитает 1 символ и сохранит ENTER в буфере ввода в следующий раз в цикле- вы увеличиваете
i
даже если символ, прочитанный пользователем, не соответствует символу вsecretWord
- когда
covered[j]
когда-нибудь быть '-'?
Я предполагаю: ваш код рассматривает перевод строки как одну из догадок при вводе данных. Я всегда избегал семейства *scanf() из-за неконтролируемой обработки ошибок. Попробуйте вместо этого использовать fgets(), а затем вытащить первый символ / байт.
Я вижу пару вещей в вашем коде:
- scanf возвращает количество прочитанных элементов. Возможно, вы захотите обработать случаи, когда он возвращает 0 или EOF.
- Я думаю, что пользователь нажимает буква + Enter, и вы получаете символ новой строки в качестве второго символа. Простой способ проверить это - добавить отладочный оператор printf, чтобы показать, какой символ был введен.
- Ваш код будет соответствовать только первому вхождению буквы соответствия, т. Е. Если бы слово было "test" и пользователь ввел "t", ваш код соответствовал бы только первому "t", а не обоим. Вы должны настроить свой первый цикл, чтобы справиться с этим.
Когда вы вводите символ, вы должны ввести символ пробела, чтобы двигаться дальше. Этот символ пробела присутствует во входном буфере, stdin
файл, и читается scanf()
функция. Эта проблема может быть решена путем использования этого дополнительного символа. Это может быть сделано с помощью getchar()
функция.
scanf("%c",¤tGuess);
getchar(); // To consume the whitespace character.
Я бы скорее предложил вам не использовать scanf()
и вместо этого использовать getchar()
, scanf()
требует много места в памяти. getchar()
это легкая функция. Так что вы также можете использовать
char currentGuess;
currentGuess=getchar();
getchar(); // To consume the whitespace character.