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" там, если вы хотите пропустить новую строку из предыдущей строки, но по-прежнему требует буквального совпадения с фиксированной строкой, как этот вопрос.

Использование scanf(" %c", &c2);, Это решит вашу проблему.

Другой вариант (который я получил отсюда) - читать и отбрасывать символ новой строки, используя опцию подавления присваивания. Для этого мы просто помещаем формат для чтения символа со звездочкой между % а также 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 одновременно, но только после того, как будет задана новая строка. Первый не возвращается, когда пользователь нажимает пробел, даже если на этом число заканчивается (потому что в этот момент строка все еще находится в строковом буфере терминала); а второй не ждет, пока вы войдете в другую строку (потому что буфер ввода уже содержит достаточно, чтобы заполнить %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();
}
Другие вопросы по тегам