Как очистить входной буфер в C?
У меня есть следующая программа:
int main(int argc, char *argv[])
{
char ch1, ch2;
printf("Input the first character:"); // Line 1
scanf("%c", &ch1);
printf("Input the second character:"); // Line 2
ch2 = getchar();
printf("ch1=%c, ASCII code = %d\n", ch1, ch1);
printf("ch2=%c, ASCII code = %d\n", ch2, ch2);
system("PAUSE");
return 0;
}
Как объяснил автор вышеприведенного кода: программа не будет работать должным образом, потому что в строке 1, когда пользователь нажимает Enter, она оставляет в буфере ввода 2 символа: Enter key (ASCII code 13)
а также \n (ASCII code 10)
, Поэтому в строке 2 он будет читать \n
и не будет ждать, пока пользователь введет символ.
ОК, я получил это. Но мой первый вопрос: почему второй getchar()
(ch2 = getchar();
) не читает Enter key (13)
, скорее, чем \n
персонаж?
Далее автор предложил 2 способа решения таких проблем:
использование
fflush()
напишите такую функцию:
void clear (void) { while ( getchar() != '\n' ); }
Этот код работал на самом деле. Но я не могу объяснить себе, как это работает? Потому что в заявлении while мы используем getchar() != '\n'
, это означает, что читать любой отдельный символ, кроме '\n'
? если так, во входном буфере все еще остается '\n'
персонаж?
20 ответов
Программа не будет работать должным образом, потому что в строке 1, когда пользователь нажимает клавишу Enter, она оставляет в буфере ввода 2 символа: клавишу ввода (код ASCII 13) и \n (код ASCII 10). Поэтому в строке 2 он будет читать \ n и не будет ждать, пока пользователь введет символ.
Поведение, которое вы видите в строке 2, правильное, но это не совсем правильное объяснение. При использовании потоков в текстовом режиме не имеет значения, какие окончания строк использует ваша платформа (возврат каретки (0x0D) + перевод строки (0x0A), пустой CR или пустой LF). Об этом позаботится библиотека времени выполнения C: ваша программа увидит только '\n'
для новых строк.
Если вы набрали символ и нажали клавишу ввода, то этот символ ввода будет читаться в строке 1, а затем '\n'
будет прочитано в строке 2. Смотрите, я использую scanf %c
читать ответ Y/N, но позже ввод пропускается. из comp.lang.c FAQ.
Что касается предлагаемых решений, см. (Снова из FAQ по comp.lang.c):
- Как можно сбросить ожидающий ввод, чтобы при следующем приглашении пользователь не читал заголовок ввода? Будет
fflush(stdin)
Работа? - Если
fflush
не будет работать, что я могу использовать для очистки ввода?
которые в основном утверждают, что единственный переносимый подход заключается в следующем:
int c;
while ((c = getchar()) != '\n' && c != EOF) { }
Ваш getchar() != '\n'
цикл работает, потому что как только вы позвоните getchar()
, возвращенный символ уже был удален из входного потока.
Кроме того, я чувствую себя обязанным отговорить вас от использования scanf
целиком: почему все говорят не использовать scanf
? Что я должен использовать вместо этого?
Вы можете сделать это (также) следующим образом:
fseek(stdin,0,SEEK_END);
Портативный способ прояснить до конца строки, которую вы уже пытались прочитать частично:
int c;
while ( (c = getchar()) != '\n' && c != EOF ) { }
Это читает и сбрасывает символы, пока не получит \n
который сигнализирует конец файла. Это также проверяет против EOF
если входной поток закрывается до конца строки. Тип c
должно быть int
(или больше), чтобы иметь возможность удерживать значение EOF
,
Нет портативного способа узнать, есть ли еще строки после текущей строки (если нет, то getchar
заблокирует для ввода).
Линии:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
;
не читает только символы до перевода строки ('\n'
). Он читает все символы в потоке (и отбрасывает их) вплоть до следующего перевода строки (или EOF встречается). Чтобы тест был верным, он должен сначала прочитать перевод строки; поэтому, когда цикл останавливается, перевод строки был последним прочитанным символом, но он был прочитан.
Что касается того, почему он читает перевод строки вместо возврата каретки, это потому, что система перевела возврат в перевод строки. Когда нажата клавиша enter, это означает конец строки... но вместо этого поток содержит перевод строки, поскольку это обычный маркер конца строки для системы. Это может зависеть от платформы.
Кроме того, используя fflush()
входной поток не работает на всех платформах; например это вообще не работает на Linux.
Но я не могу объяснить себе, как это работает? Потому что в заявлении while мы используем
getchar() != '\n'
, это означает, что читать любой отдельный символ, кроме'\n'
?? если так, во входном буфере все еще остается'\n'
персонаж??? Я что-то недопонимаю??
Вещь, которую вы не можете понять, что сравнение происходит после того, какgetchar()
удаляет символ из входного буфера. Поэтому, когда вы достигнете '\n'
, он потребляется, а затем вы выходите из цикла.
- странная функция, и здесь уместна классическая фраза из фильма « Военные игры »: «Единственный выигрышный ход - не играть».
Если вам нужно «смыть ввод», вы уже проиграли . Выигрышный ход состоит не в том, чтобы отчаянно искать какой-то волшебный способ избавиться от неприятного ввода: вместо этого вам нужно сделать ввод более качественным способом, который не предполагает оставлять непрочитанный ввод во входном потоке и иметь его. вызвать такие проблемы, что вам придется попытаться его промыть.
В основном есть три случая:
Вы читаете ввод, используя, и он оставляет пользовательский символ новой строки на вводе, и этот случайный символ новой строки ошибочно читается последующим вызовом или. (Это тот случай, о котором вы изначально спрашивали.)
Вы читаете ввод, используя, и он оставляет пользовательский символ новой строки на вводе, и этот случайный символ новой строки ошибочно читается последующим вызовом.
Вы читаете числовой ввод с помощью, а пользователь вводит нечисловой ввод, и нечисловой ввод остается на вводе, что означает, что следующий вызов также не удастся.
Во всех трех случаях может показаться, что правильным будет «очистить» вводимые данные оскорбительного характера. И вы можете попробовать, но это в лучшем случае громоздко, а в худшем - невозможно. В конце концов, я считаю, что попытка смыть ввод - неправильный подход, и что есть способы получше, в зависимости от того, о каком случае вы беспокоились:
В случае 1 лучшим решением будет не смешивать вызовы с другими функциями ввода . Либо делайте весь свой ввод с помощью, либо делайте весь свой ввод с помощью и / или. Чтобы выполнить весь свой ввод с помощью, вы можете заменить вызовы на
В случае 2 лучшим решением будет никогда не использовать
scanf("%c")
. Использовать
А в случае 3, боюсь, хорошего решения просто нет. имеет много проблем, и одна из многих проблем заключается в ужасной обработке ошибок. Если вы хотите написать простую программу, которая считывает числовой ввод, и если вы можете предположить, что пользователь всегда будет вводить правильный числовой ввод, когда будет предложено, тогда
Вы заметите, что тема проходит во всех трех перечисленных мною случаях: все они начинались со слов «Вы читаете ввод, используя
Теперь я понимаю, что до сих пор не ответил на вопрос, который вы на самом деле задали. Когда люди спрашивают: «Как мне сделать X?», Это может быть очень неприятно, когда все ответы: «Тебе не следует делать X». Если вы действительно, действительно хотите очистить ввод, то, помимо других ответов, которые вам здесь дали, есть еще два хороших вопроса, полных соответствующих ответов:
Ты можешь попробовать
scanf("%c%*c", &ch1);
где%*c принимает и игнорирует перевод строки
еще один метод вместо fflush(stdin), который вызывает неопределенное поведение, которое вы можете написать
while((getchar())!='\n');
не забывайте точку с запятой после цикла while
Как я могу сбросить или очистить входной буфер в C?
Ссылка на вики сообщества cppreference.com гласит (выделено мной):
Для входных потоков (и для потоков обновления, для которых была введена последняя операция) поведение не определено.
Итак, не используйте
fflush()
на .
Если ваша цель "сброса" состоит в том, чтобы удалить все символы, находящиеся в буфере, то лучший способ сделать это - вручную либо с помощью getchar()
или же getc(stdin)
(то же самое), или, может быть, с read()
(с использованием в качестве первого аргумента) при использовании POSIX или Linux.
Наиболее популярные ответы здесь и здесь делают это с помощью:
int c;
while ((c = getchar()) != '\n' && c != EOF);
Я думаю, что более четкий (более читаемый) способ - сделать это так. Мои комментарии, конечно, делают мой подход намного длиннее, чем он есть:
/// Clear the stdin input stream by reading and discarding all incoming chars up
/// to and including the Enter key's newline ('\n') char. Once we hit the
/// newline char, stop calling `getc()`, as calls to `getc()` beyond that will
/// block again, waiting for more user input.
/// - I copied this function
/// from "eRCaGuy_hello_world/c/read_stdin_getc_until_only_enter_key.c".
void clear_stdin()
{
// keep reading 1 more char as long as the end of the stream, indicated by
// `EOF` (end of file), and the end of the line, indicated by the newline
// char inserted into the stream when you pressed Enter, have NOT been
// reached
while (true)
{
int c = getc(stdin);
if (c == EOF || c == '\n')
{
break;
}
}
}
Например, я использую эту функцию в своих двух файлах. См. контекст в этих файлах при очистке
stdin
может быть наиболее полезным:
Примечание для себя: я изначально разместил этот ответ здесь , но с тех пор удалил этот ответ, чтобы вместо этого оставить этот как мой единственный ответ.
Другое решение, которое еще не упомянуто, заключается в использовании: перемотка (stdin);
Я столкнулся с проблемой, пытаясь реализовать решение
while ((c = getchar()) != '\n' && c != EOF) { }
Я публикую небольшую корректировку "Код B" для тех, у кого может быть такая же проблема.
Проблема заключалась в том, что программа заставляла меня ловить символ '\n', независимо от символа ввода, вот код, который дал мне проблему.
Код А
int y;
printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
continue;
}
printf("\n%c\n", toupper(y));
и корректировка заключалась в том, чтобы "поймать" (n-1) символ непосредственно перед вычислением условия в цикле while, вот код:
Код Б
int y, x;
printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
x = y;
}
printf("\n%c\n", toupper(x));
Возможное объяснение состоит в том, что для прерывания цикла while необходимо присвоить значение '\n' переменной y, поэтому оно будет последним присвоенным значением.
Если я что-то пропустил с объяснением, кодом A или кодом B, пожалуйста, скажите мне, я едва новичок в c.
надеюсь, это поможет кому-то
unsigned char a=0;
if(kbhit()){
a=getch();
while(kbhit())
getch();
}
cout<<hex<<(static_cast<unsigned int:->(a) & 0xFF)<<endl;
-или же-
использовать возможно использовать _getch_nolock()
..???
char choice;
do{
printf("\n *");
printf("\n Do you want to continue? (y/n) ");
choice = getchar();
if(getchar() != '\n'){
fflush(stdin);
}
}while( choice != 'n' );
Быстрое решение - добавить второй scanf, чтобы он заставил ch2 временно съесть возврат каретки. Не выполняет никаких проверок, поэтому предполагает, что пользователь будет вести себя хорошо. Не совсем очищает входной буфер, но работает нормально.
int main(int argc, char *argv[])
{
char ch1, ch2;
printf("Input the first character:"); // Line 1
scanf("%c", &ch1);
scanf("%c", &ch2); // This eats '\n' and allows you to enter your input
printf("Input the second character:"); // Line 2
ch2 = getchar();
printf("ch1=%c, ASCII code = %d\n", ch1, ch1);
printf("ch2=%c, ASCII code = %d\n", ch2, ch2);
system("PAUSE");
return 0;
}
Во-первых, вам следует использовать переменную типа int вместо char для чтения из потоков ввода-вывода (по крайней мере, в Linux).
Во-вторых, после первого чтения в Linux в стандартном вводе остается ТОЛЬКО '\n' (в Windows все по-другому). Таким образом, вы можете использовать дополнительную переменную для очистки буфера после первого чтения в вашем примере. Например, ваш код может выглядеть следующим образом:
int cr;
cr = fgetc(stdin);
Просто для полноты, в вашем случае, если вы действительно хотите использовать, что есть много причин не использовать, я бы добавил пробел перед спецификатором формата, говоряscanf
игнорировать все пробелы перед символом:
scanf(" %c", &ch1);
Подробнее см. здесь: https://en.cppreference.com/w/c/io/fscanf#Notes
Вкратце. Ставим линию ...
while ((c = getchar()) != '\n') ;
... до чтения строки единственный гарантированный метод. Он использует только основные функции C («обычное ядро языка C» согласно K&R), которые гарантированно работают со всеми компиляторами при любых обстоятельствах.
На самом деле вы можете добавить подсказку, предлагающую пользователю нажать ENTER для продолжения (или, при желании, нажать Ctrl-D или любую другую кнопку, чтобы закончить или выполнить другой код):
printf("\nPress ENTER to continue, press CTRL-D when finished\n");
while ((c = getchar()) != '\n') {
if (c == EOF) {
printf("\nEnd of program, exiting now...\n");
return 0;
}
...
}
Проблема все еще существует. Пользователь может нажимать ENTER много раз. Это можно обойти, добавив проверку ввода в строку ввода:
while ((ch2 = getchar()) < 'a' || ch1 > 'Z') ;
Комбинация двух вышеперечисленных методов теоретически должна быть пуленепробиваемой. Во всех остальных аспектах ответ @jamesdlin является наиболее полным.
Я написал функцию (и протестировал ее), которая получает ввод со стандартного ввода и отбрасывает лишний ввод (символы). Эта функция называется get_input_from_stdin_and_discard_extra_characters(char *str, int size) и считывает максимальное количество символов "size - 1" и добавляет "\0" в конце.
Код ниже:
/* read at most size - 1 characters. */
char *get_input_from_stdin_and_discard_extra_characters(char *str, int size)
{
char c = 0;
int num_chars_to_read = size - 1;
int i = 0;
if (!str)
return NULL;
if (num_chars_to_read <= 0)
return NULL;
for (i = 0; i < num_chars_to_read; i++) {
c = getchar();
if ((c == '\n') || (c == EOF)) {
str[i] = 0;
return str;
}
str[i] = c;
} // end of for loop
str[i] = 0;
// discard rest of input
while ((c = getchar()) && (c != '\n') && (c != EOF));
return str;
} // end of get_input_from_stdin_and_discard_extra_characters
Короткий, портативный и объявленный в stdio.h
stdin = freopen(NULL,"r",stdin);
Не зацикливаться на бесконечном цикле, когда на stdin нет ничего для сброса, как в следующей хорошо известной строке:
while ((c = getchar()) != '\n' && c != EOF) { }
Немного дорого, так что не используйте его в программе, которой нужно многократно очищать буфер.
Украл у коллеги:)