[Язык 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), однако перед тем, как ее использовать обязательно ознакомьтесь с документацией.

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


Запись опубликована в рубрике С (Си). Добавьте в закладки постоянную ссылку.

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

  1. Леонид говорит:

    Символьный пробел перед спецификатором в ф-ции scanf() позволяется обойтись от цикла while.

    #include

    int main (void)
    {
    char z, x;

    // Запрос первого символа
    printf(«Enter the first letter: «);
    scanf (» %c», &z); printf(«%c\n», z);

    // Запрос второго символа
    printf(«Enter the second letter: «);
    scanf (» %c», &x); printf(«%c», x);

    printf(«\nThe program is completed.\n»);

    return 0;
    }

Добавить комментарий

🇬🇧 Attention! Comments with URLs/email are not allowed.
🇷🇺 Комментарии со ссылками/email удаляются автоматически.