scanf () оставляет символ новой строки в буфере


87

У меня есть такая программа:

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;
}

Как я читал в книге C, автор говорит, что scanf()оставил символ новой строки в буфере, поэтому программа не останавливается на строке 4 для ввода данных пользователем, скорее она сохраняет символ новой строки в c2 и переходит к строка 5.

Это правильно?

Однако происходит ли это только с charтипами данных? Потому что я не видел этой проблемы с intтипами данных, как в строке 1, 2, 3. Верно?


Иногда предлагается fflush(stdin)использовать его перед вызовом scanf()для одного символа. Пожалуйста, прочтите Использованиеfflush(stdin) для обсуждения плюсов и минусов, а также альтернатив этому методу (который более или менее работает в Windows и не работает в большинстве других мест).
Джонатан Леффлер

Не могли бы вы сообщить нам, о какой книге вы имеете в виду?
Сурья Киран

Ответы:


78

В scanf()функции пропускает ведущие пробелы автоматически , прежде чем пытаться разобрать , кроме символов преобразования. Форматы символов (в первую очередь %c; также наборы сканирования %[…]- и %n) являются исключением; они не пропускают пробелы.

Используйте " %c"с пробелом в начале, чтобы пропустить необязательный пробел. Не используйте завершающий пробел в scanf()строке формата.

Обратите внимание, что это по-прежнему не использует никаких конечных пробелов, оставшихся во входном потоке, даже до конца строки, поэтому остерегайтесь этого, если вы также используете getchar()или fgets()в том же входном потоке. Мы просто заставляем scanf пропускать пробелы перед преобразованиями, как это делается для %dи других несимвольных преобразований.


Обратите внимание, что «директивы» без пробелов (для использования терминологии POSIX scanf ), отличные от преобразований, такие как буквальный текст в scanf("order = %d", &order);, также не пропускают пробелы. Литерал orderдолжен соответствовать следующему символу для чтения.

Так что вы, вероятно, захотите " order = %d"туда, если хотите пропустить новую строку из предыдущей строки, но все же требуете буквальное совпадение с фиксированной строкой, как этот вопрос .


7
%c, %n, %[]Являются 3 специфицированные ожидания , которые не потребляют ведущие пробелы.
chux

@chux Так поступает ли в других случаях, scanf очищает все пробелы до этого в буфере или игнорирует их для ввода, но они все еще там?
Сурадж Джайн

@SurajJain Да,
chux

3
См. Завершающий пробел в scanf()строке формата и scanf()запрос дважды на ввод, хотя я ожидаю, что он запросит только один раз для обсуждения конечных пробелов в строках формата. Это плохая идея - поразительно плохая, если вы ожидаете человеческого взаимодействия, и плохая для взаимодействия с программой.
Джонатан Леффлер


7

Другой вариант (который я получил отсюда ) - прочитать и отбросить новую строку с помощью параметра присваивания-подавления . Для этого мы просто устанавливаем формат для чтения символа со звездочкой между %и c:

scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3

scanf затем прочитает следующий символ (то есть новую строку), но не назначит его никакому указателю.

В конце концов, однако, я бы поддержал последний вариант FAQ :

Или, в зависимости от ваших требований, вы также можете забыть о scanf () / getchar (), использовать fgets (), чтобы получить строку текста от пользователя и проанализировать ее самостоятельно.


2
Проблема с этой техникой заключается в том, что если пользователь вводит aзатем пробел, а затем новую строку, подавленное преобразование символов считывает пробел и по-прежнему оставляет новую строку. Если пользователь набирает, supercalifragilisticexpialidociousкогда вы ожидаете a, у вас есть много лишних символов, с которыми нужно иметь дело. Вы также никогда не можете сказать, успешна ли завершающая подавленная конверсия - они не учитываются в возврате из scanf().
Джонатан Леффлер,

3

Используйте getchar()перед вызовом второго scanf().

scanf("%c", &c1);
getchar();  // <== remove newline
scanf("%c", &c2);

1
Это работает при условии, что пользователь больше ничего не вводил - например, завершающие пробелы. Но это не так хорошо, как цикл, который сканирует до следующей новой строки: int c; while ((c = getchar()) != EOF && c != '\n') ;(записывается на трех строках, когда не в комментарии). Часто этого бывает достаточно; это не надежно (и вы должны помнить, что дураки очень умны в том, чтобы ломать вещи).
Джонатан Леффлер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.