scanf() оставляет новую строку char в буфере
У меня есть следующая программа:
int main(int argc, char *argv[])
{
int a, b;
char c1, c2;
printf("Enter something: ");
scanf("%d",&a); // line 1
printf("Enter other something: ");
scanf("%d", &b); // line 2
printf("Enter a char: ");
scanf("%c",&c1); // line 3
printf("Enter another char: ");
scanf("%c", &c2); // line 4
printf("Done"); // line 5
system("PAUSE");
return 0;
}
Как я читал в книге С, автор говорит, что scanf()
оставил символ новой строки в буфере, поэтому программа не останавливается на строке 4, чтобы пользователь мог ввести данные, скорее она сохраняет символ новой строки в c2 и перемещается на строку 5.
Это правильно?
Однако происходит ли это только с char
типы данных? Потому что я не видел эту проблему с int
типы данных как в строке 1, 2, 3. Это правильно?
7 ответов
scanf()
Функция автоматически удаляет пробелы, прежде чем пытаться анализировать преобразования, отличные от символов. Форматы символов (в первую очередь %c
; также сканирует наборы %[…]
- а также %n
) являются исключением; они не удаляют пробелы.
использование " %c"
с пробелом в начале, чтобы пропустить необязательный пробел. Не используйте завершающий бланк в scanf()
строка формата.
Обратите внимание, что он по-прежнему не использует конечные пробелы, оставшиеся во входном потоке, даже до конца строки, поэтому остерегайтесь этого, если также используете getchar()
или же fgets()
на том же входном потоке. Мы просто получаем scanf для пропуска пробелов перед конверсией, как это делается для %d
и другие не символьные преобразования.
Обратите внимание, что непропускные "директивы" (для использования терминологии scanf для POSIX), кроме преобразований, такие как буквальный текст в scanf("order = %d", &order);
также не пропускает пробелы. Буквальный order
должен соответствовать следующий символ для чтения.
Так что вы, вероятно, хотите " order = %d"
там, если вы хотите пропустить новую строку из предыдущей строки, но по-прежнему требует буквального совпадения с фиксированной строкой, как этот вопрос.
Другой вариант (который я получил отсюда) - читать и отбрасывать символ новой строки, используя опцию подавления присваивания. Для этого мы просто помещаем формат для чтения символа со звездочкой между %
а также c
:
scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3
scanf
затем прочитает следующий символ (то есть символ новой строки), но не назначит его ни одному указателю.
В конце, однако, я бы поддержал последний вариант FAQ:
Или, в зависимости от ваших требований, вы также можете забыть о scanf()/getchar(), использовать fgets(), чтобы получить строку текста от пользователя и проанализировать ее самостоятельно.
Чтобы повторить то, что я опубликовал в другом ответе о C++: я предлагаю выбросить, никогда не использовать его и вместо этого использовать и (*).
Причина этого в том, что по крайней мере в Unix-подобных системах по умолчанию терминал, на котором работает ваша программа CLI, выполняет некоторую обработку пользовательского ввода до того, как ваша программа его увидит. Он буферизует ввод до тех пор, пока не будет введена новая строка, и позволяет выполнить элементарное редактирование строки, например, заставить работать backspace.
Таким образом, вы никогда не сможете получить один символ за раз или несколько отдельных символов, а только целую строку. Но это не то, что, например, обрабатывает, вместо этого он обрабатывает только цифры и останавливается на этом , оставляя остальное в буфере в библиотеке C на будущее.
stdio
printf("Enter a number: ");
scanf("%d", &a);
printf("Enter a word: ");
scanf("%s", word);
и вы входите в линию
123 abcd
%s
конверсия).
Это не то, чего обычно ожидают пользователи!
Вместо этого они ожидают, что нажатие Enter завершит ввод, и если вы нажмете Enter, вы получите либо значение по умолчанию, либо ошибку, с возможным предложением просто дать ответ.
Вы действительно не можете этого сделать с. Если пользователь просто нажимает Enter, ничего не происходит. Потому что еще жду номер. Терминал отправляет строку дальше, но ваша программа ее не видит, потому что ест ее. У вас нет возможности отреагировать на ошибку пользователя.
Это тоже не очень полезно.
Следовательно, я предлагаю использовать или
getline()
atoi()
,
strtol()
, или действительно
sscanf(buf, "%d", &a)
. не имеет такого же несоответствия, как
scanf()
, потому что буфер, из которого она читает, ограничен по размеру, и когда он заканчивается, он заканчивается - функция не может ждать большего.
(
fscanf()
Итак, вместо того, что у меня было выше, используйте что-то вроде этого:
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
sscanf(buf, "%d", &a);
или, на самом деле, также проверьте возвращаемые значения, чтобы мы могли обнаружить пустые строки или другие недопустимые данные:
#include <stdio.h>
int main(void)
{
const int bufsize = 100;
char buf[bufsize];
int a;
int ret;
char word[bufsize];
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%d", &a);
if (ret != 1) {
fprintf(stderr, "Ok, you don't have to\n");
return 1;
}
printf("Enter a word: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%s", word);
if (ret != 1) {
fprintf(stderr, "You make me sad.\n");
return 1;
}
printf("You entered %d and %s\n", a, word);
}
Конечно, если вы хотите, чтобы программа настаивала, вы можете создать простую функцию, которая будет перебирать и
sscanf()
Вы можете сделать что-то подобное, например, зациклившись
getchar()
scanf("%d")
возвращается, таким образом очищая весь мусор, оставшийся в буфере, но это ничего не делает в случае, когда пользователь просто нажимает ввод в пустой строке. Так или иначе,
fgets()
будет читать до новой строки, поэтому вам не нужно делать это самостоятельно.
Использование getchar()
перед вызовом второго scanf()
,
scanf("%c", &c1);
getchar(); // <== remove newline
scanf("%c", &c2);
Два обходных пути. Один из них - ловить лишние пробелы.
getchar(); // (1) add `getchar()`
scanf("%c", &c1);
scanf(" %c", &c1); // (2) add whitespace before %c
Другой используетfgets()
вместо . Примечание:gets()
небезопасно, узнайте, почему gots опасно
Напротив, я бы предпочел порекомендовать второй путь. Потому что это более читабельно и ремонтопригодно . Например, в первом случае вы должны подумать, прежде чем добавлять/перемещать некоторыеscanf()
.
/*Take char input using scanf after int input using scanf just use fflush(stdin) function after int input */
#include<stdio.h>
#include<conio.h>
void main()
{
int x;
char y;
clrscr();
printf(" enter an int ");
scanf("%d",&x);
fflush(stdin);
printf("\n Now enter a char");
scanf("%c",&y);
printf("\n X=%d and Y=%c",x,y);
getch();
}