Как 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, включая пробелы.