Menu

Ввод и вывод в C++: cin, cout, getline и потоки

Как работает консольный ввод-вывод в C++: вывод через cout, чтение через cin, классический баг с переводом строки в getline после cin и как восстановиться, когда ввод не удался.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Как C++ общается с консолью

C++ выполняет консольный ввод-вывод через потоки из заголовка <iostream>. Вы пишете во внешний мир через cout (символьный вывод) и читаете от пользователя через cin (символьный ввод). Они используют два оператора, которые вы уже видели как побитовые сдвиги, здесь переосмысленные:

  • << — оператор вставки: он отправляет данные в cout.
  • >> — оператор извлечения: он вытаскивает данные из cin в переменную.

Удобный способ запомнить направление: стрелки указывают в ту сторону, куда движутся данные. Теперь, когда вы умеете хранить текст в строках, давайте перемещать этот текст в вашу программу и обратно.

Вывод через cout

cout отправляет в стандартный вывод всё, что вы вставляете. Можно сцеплять несколько << в одной инструкции, свободно смешивая текст, числа и переменные.

Каждый << добавляется к той же строке, пока вы не вставите перевод строки. Сделать это можно двумя способами: '\n' вставляет символ перевода строки, а endl вставляет перевод строки и сбрасывает буфер на экран. У сброса есть реальная цена, поэтому внутри цикла, печатающего тысячи строк, предпочитайте '\n' — поток сбрасывается сам, когда нужно (и всегда при завершении программы).

// Нормально для разового сообщения:
cout << "Done" << endl;

// В горячем цикле предпочтите это - без принудительного сброса на каждой итерации:
for (int i = 0; i < 1000000; ++i)
    cout << i << '\n';

Чтение через cin и >>

cin >> variable читает один токен, ограниченный пробельными символами, и преобразует его в тип переменной. Он пропускает любые ведущие пробелы или переводы строки, а затем останавливается на следующем пробельном символе.

Поскольку >> останавливается на пробеле, он отлично подходит для отдельных чисел и одиночных слов, но бесполезен для целого предложения: cin >> word на вводе hello world читает только hello и оставляет world в буфере для следующего чтения.

Чтение целой строки через getline

Чтобы захватить целую строку — вместе с пробелами — используйте getline(cin, line), который читает всё до клавиши Enter в std::string.

Это правильный инструмент всякий раз, когда ввод может содержать пробелы — имена, адреса, предложения. Есть лишь одна ловушка, которая поджидает вас в тот момент, когда вы смешиваете getline с >>.

Ловушка с переводом строки между cin и getline

Это самый распространённый баг ввода-вывода в C++. Когда вы делаете cin >> n, извлечение читает число, но оставляет перевод строки (клавишу Enter, которую вы нажали) в буфере ввода. Следующий getline сразу видит этот оставшийся перевод строки, считает строку уже завершённой и выдаёт вам пустую строку — не останавливаясь ни на миг для ожидания ввода.

int age;
string city;

cin >> age;            // вы вводите 30 и нажимаете Enter; '\n' остаётся в буфере
getline(cin, city);    // читает оставшийся '\n' -> city становится "" (пустой!)

Решение — отбросить этот оставшийся перевод строки с помощью cin.ignore после >> и перед getline:

cin.ignore(numeric_limits<streamsize>::max(), '\n') пропускает символы, пока не «съест» перевод строки (или не достигнет конца ввода). Это надёжная версия — более короткий cin.ignore() отбрасывает лишь один символ, что ломается, если пользователь ввёл лишние пробелы после числа. По привычке используйте полную форму.

Когда ввод не удался

Если пользователь вводит буквы там, где вы ожидали число, извлечение не удаётся: cin переходит в состояние ошибки, а целевая переменная остаётся неизменной (с C++11 устанавливается в 0). Хуже того, как только cin оказывается в состоянии сбоя, каждое последующее чтение тоже молча пропускается, поэтому вы можете оказаться в бесконечном цикле.

Восстановление всегда состоит из двух шагов: cin.clear() сбрасывает флаги ошибки, чтобы поток снова стал пригодным к использованию, а cin.ignore(...) выбрасывает проблемные символы, всё ещё застрявшие в буфере. Пропустите ignore — и неверный ввод останется на месте, так что следующий >> снова провалится: классический бесконечный цикл. Проверка cin >> n прямо в условии работает, потому что поток преобразуется в false, когда находится в состоянии сбоя.

Частые ошибки, которых стоит избегать

  • Использование cin >> s для предложения. Он останавливается на первом пробеле. Используйте getline для всего, что содержит пробелы.
  • Забыть cin.ignore между >> и getline. Оставшийся перевод строки даёт вам пустую строку. Сначала очистите буфер.
  • Хвататься за endl повсюду. Каждый из них форсирует сброс. По умолчанию используйте '\n' и приберегите endl для случаев, когда вывод действительно должен появиться сейчас.
  • Игнорировать сбойный cin. Буквы при числовом чтении ломают cin; всегда делайте clear(), а затем ignore() перед повторным чтением.

Далее: Строковые потоки

Консольный ввод-вывод и работа со строками сходятся в строковых потоках. stringstream даёт вам те же операторы << и >>, но направленные на строку в памяти, а не на консоль — идеально для разбора строки на числа, сборки форматированного текста и преобразования между строками и другими типами, не касаясь клавиатуры.

Часто задаваемые вопросы

Почему getline пропускает ввод сразу после использования cin в C++?

cin >> x читает число, но оставляет в буфере перевод строки, который вы нажали. Следующий getline читает до этого оставшегося перевода строки и сразу возвращает пустую строку. Сначала очистите его с помощью cin.ignore(numeric_limits<streamsize>::max(), '\n'); после >> и перед getline.

В чём разница между endl и \n в C++?

Оба завершают строку, но endl ещё и сбрасывает (flush) буфер вывода на экран, тогда как '\n' просто вставляет перевод строки. Сброс стоит дорого: в плотном цикле предпочитайте '\n' и позвольте потоку сбрасываться самому. Используйте endl только когда вам действительно нужно, чтобы вывод появился прямо сейчас.

Как прочитать целую строку текста вместе с пробелами в C++?

Используйте getline(cin, line), а не cin >> line. Оператор >> останавливается на первом пробельном символе, поэтому захватывает только одно слово. getline читает всё до клавиши Enter в std::string, включая пробелы.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ