[Язык C] Коварные символы новой строки на вводе

При выполнении последнего упражнения в главе 7, столкнулся с проблемой считывания строки ввода, если в ней встречается перевод строки. Везде это объясняется несколько заумно, попробую на пальцах:

Проблема в том, что когда вы вводите с клавиатуры в scanf любой ввод – помимо символов (букв, цифр и проч) – вы также _всегда_ отправляете во входящий поток символ /n – то бишь перевод строки.

К примеру, в программе стоит оператор scanf ("%c", &n);

Вы нажимаете на клавиатуре клавишу q, а затем клавишу Enter, чтобы отправить значение в программу…

Что при этом оказывается в программе? Логично предположить, что там теперь будет только q. Действительно, q становится значением переменной n. Но на самом деле кое-что еще – вы отправили в поток ввода (он еще зовётся stdin) и клавишу Enter, которую можно интерпретировать как \n.. Возьмем еще один пример:

#include <stdio.h>
int main (void)
{
int z, x;

scanf ("%c", &z);
printf("%c", z);

scanf ("%c", &x);
printf("%c", x);

return 0;
}

Теперь запустим программу и…

  1. Нажмем клавишу a, затем нажмем клавишу Enter
  2. На экран будет выведен символ a
  3. Теперь нажмем клавишу b, затем нажмем клавишу Enter
  4. На экран не будет выведена b, будет лишь пустая строка

А вот таже самая ситуация глазами программы:

  1. Запрашиваю значение переменной z
  2. Пользователь вводит a и \n
  3. Присваиваю переменной z значение a
  4. Вывожу на экран a
  5. Запрашиваю значение переменной x
  6. Пользователь вводит b и \n
  7. Вижу, что в входном потоке находится \n b \n
  8. Беру следующий символ из потока ввода: \n и присваиваю его переменной x
  9. Вывожу на экран символ новой строки (\n)
  10. При этом в потоке все также остаются b \n

…которые так и будут вечно лежать в этом буфере (так называют входной поток, который уже сохранен в памяти программы).

Что делать, чтобы таких ошибок не возникало?

  1. помнить, что scanf (и само собой getchar) не пропускает служебные символы (в том числе \n) при вводе символов %c, а также %[…] и %n. В остальных случаях можно не париться, к примеру, scanf ("%d", x) – не обращает на \n внимания (поэтому ни в коем случае не используйте что-то вроде scanf ("%d\n", x)).
  2. чистить буфер. Как говорится: чистота – залог здоровья (пойти принять душ что ли?). Для этого есть много способов, начинающим рекомендую делать так:
    while (getchar() != '\n')
    	continue;

    Что при этом происходит – цикл вынимает по одному символы из входного потока и сравнивает – не перенос ли это строки. Если нет – то он переходит к continue;, которая возвращает программу в начало цикла и снова достает еще один символ и снова сравнивает. И так до тех пор, пока не встретит \n; при этом, когда он таки встретит \n – цикл уже его из буфера достанет (т.е. буфер очистит, он станет пустым), и цикл прекратится → ваша программа продолжит выполнение.

    Есть и другие способы чистки буфера, например, функция fflush(stdin), однако перед тем, как ее использовать обязательно ознакомьтесь с документацией.

Буду рад вашим комментариям!

This entry was posted in С (Си). Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *