scanf () залишає новий рядок char у буфері


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 і не працює в більшості інших місць).
Джонатан Леффлер,

Не могли б ви повідомити нас, до якої книги ви маєте на увазі.?
surya kiran

Відповіді:


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 очищає всі пробіли раніше в буфері або ігнорує їх, оскільки вони вводять, але вони все ще там?
Suraj Jain

@SurajJain Так,
chux

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

31

Використовуйте scanf(" %c", &c2);. Це вирішить вашу проблему.


7

Інший варіант (який я отримав звідси ) - прочитати та відкинути новий рядок, використовуючи опцію призначення-придушення . Для цього ми просто поставили формат для читання символу із зірочкою між %та c:

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

scanf потім прочитає наступний символ (тобто новий рядок), але не призначить його жодному покажчику.

Врешті-решт, я б підтримав останній варіант часто заданих питань :

Або, залежно від ваших вимог, ви також можете забути про 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.