Чтение целой строки
cin >> text читает одно слово: он останавливается на первом пробеле. Чтобы прочитать всё, что пользователь ввёл до клавиши Enter, вместе с пробелами, используйте std::getline из заголовка <string>:
#include <iostream>
#include <string>
int main() {
std::string name;
std::cout << "Full name: ";
std::getline(std::cin, name);
std::cout << "Hello, " << name << "!\n";
}
Если ввести Ada Lovelace, программа напечатает Hello, Ada Lovelace!. С std::cin >> name она напечатала бы Hello, Ada! и оставила бы Lovelace ждать во вводе.
Сигнатура такая:
std::istream& std::getline(std::istream& input, std::string& text);
std::istream& std::getline(std::istream& input, std::string& text, char delim);
inputэто любой поток ввода:std::cin, файл, открытый черезstd::ifstream, илиstd::istringstream, построенный из строки.textзаменяется прочитанными символами (а не дополняется ими).delimэто символ, на котором чтение заканчивается. По умолчанию это'\n'. Разделитель удаляется из потока и не сохраняется вtext.- Возвращается сам поток, и именно поэтому его можно проверять в условии.
Поскольку getline одинаково работает с любым потоком ввода, запускаемые примеры на этой странице читают из std::istringstream с заранее заданным текстом. Так вывод предсказуем, и ничего не нужно вводить. Чтобы читать с клавиатуры, замените переменную потока на std::cin.
Вывод:
[Ada Lovelace]
[Alan Turing]
Квадратные скобки показывают, что перевод строки не входит ни в одну из строк.
getline после cin >> возвращает пустую строку
Это самая частая проблема с getline. Вы читаете число через >>, затем строку через getline, и getline будто бы пропускается:
Вывод:
age: 30
city: []
input >> age читает символы 30 и останавливается на первом символе, который не может быть частью числа, то есть на переводе строки. Этот перевод строки остаётся в потоке. Затем getline читает до следующего перевода строки, сразу его находит и возвращает пустую строку. С std::cin это выглядит так, будто программа не останавливается, чтобы дождаться второго ответа.
Есть два стандартных исправления. Оба ставятся между >> и getline.
Исправление 1: отбросить остаток строки через ignore.
Вывод:
age: 30
city: [Paris]
ignore(n, '\n') выбрасывает символы, пока не удалит перевод строки или n символов, смотря что наступит раньше. Самое большое возможное значение std::streamsize означает «до перевода строки, как бы далеко он ни был». Заодно удаляется всё, что пользователь ввёл после числа в той же строке, например пробелы в конце. Для клавиатуры строка выглядит как std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');, и ей нужен #include <limits>.
Просто std::cin.ignore() отбрасывает ровно один символ. Это работает, когда перевод строки идёт сразу после числа, и перестаёт работать, как только перед ним оказывается пробел.
Исправление 2: пропустить ведущие пробельные символы через std::ws.
Вывод:
age: 30
city: [Paris]
std::ws это манипулятор, который поглощает все пробельные символы (пробелы, табуляции, переводы строки) в текущей позиции. input >> std::ws возвращает поток, поэтому его можно сразу передать в getline. Он также пропускает пустые строки и ведущие пробелы. Для имён и городов это удобно, но неправильно, когда ведущие пробелы или пустая строка это значимый ввод. В таком случае используйте ignore.
Чтение до конца ввода
getline возвращает поток, а поток превращается в false, как только чтение не удалось. Когда строки заканчиваются, getline завершается неудачей, поэтому цикл while вокруг него читает каждую строку и останавливается в конце:
Вывод:
1: [apples 3]
2: [bread 1]
3: []
4: [milk 2]
read 4 lines
Две детали из этого вывода:
- Пустая строка тоже успешное чтение. Она даёт пустую строку, и цикл продолжается. Остановить его может только конец ввода.
- Последней строке не нужен завершающий перевод строки. Если бы текст заканчивался на
milk 2без\n,getlineвсё равно вернул бы её, а следующий вызов завершился бы неудачей.
С std::cin тот же цикл читает, пока ввод не закончится. Это происходит, когда ввод перенаправлен из файла (./program < data.txt) или когда пользователь нажимает Ctrl+D в macOS и Linux либо Ctrl+Z и затем Enter в Windows.
Избегайте шаблона while (!input.eof()) { std::getline(input, line); ... }. eof() становится истинным только после того, как чтение уже упёрлось в конец, поэтому тело цикла выполняется лишний раз с неудачным чтением и устаревшим line. Вместо этого поместите вызов getline в условие.
Построчное чтение файла
Для файлов подходит тот же цикл. Откройте файл через std::ifstream из <fstream>, проверьте, что он открылся, и читайте:
Вывод:
1. Ada 92
2. Grace 88
3. Alan 95
if (!file) ловит отсутствующий файл или неверный путь до начала цикла. Без этой проверки цикл просто прочитает ноль строк, и это будет выглядеть как пустой файл, а не как ошибка. Файл закрывается автоматически, когда file выходит из области видимости в конце main.
Файлы, сохранённые в Windows, заканчивают каждую строку на \r\n. Когда такой файл читают в Linux или macOS, getline удаляет \n, но оставляет \r, и каждая строка заканчивается невидимым возвратом каретки, который ломает сравнения вроде line == "done". Удаляйте его после чтения:
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
Свой разделитель
Третий аргумент меняет символ, на котором заканчивается каждое чтение. Обычно так разбивают одну строку по запятым. Сначала прочитайте строку, затем разбейте её через std::istringstream:
Вывод:
fields: 3
[Ada Lovelace]
[36]
[London]
Запятая удаляется из потока и не сохраняется, так же как перевод строки в форме по умолчанию. Учтите, что со своим разделителем перевод строки становится обычным символом. Если вызвать std::getline(file, field, ',') прямо на многострочном файле, последнее поле одной строки и первое поле следующей вернутся склеенными, как в "London\nGrace Hopper". Чтобы этого избежать, читайте по одной строке и разбивайте каждую отдельно. Подробнее std::istringstream, включая преобразование полей вроде "36" в числа, разобран на странице про строковые потоки.
std::getline против cin.getline
Есть ещё функция-член с похожим именем:
char buffer[20];
std::cin.getline(buffer, 20); // функция-член, массив char фиксированного размера
std::string text;
std::getline(std::cin, text); // свободная функция, std::string
cin.getline записывает в массив char и сохраняет не больше size - 1 символов плюс завершающий '\0'. Более длинная строка переводит поток в состояние ошибки, и каждое следующее чтение пропускается, пока вы не вызовете std::cin.clear(). У std::getline со std::string нет ограничения длины, за которым надо следить. Используйте его, если только вы не работаете с API, которому нужен массив char.
Частые ошибки
- Смешивать
>>иgetline, не убрав перевод строки. Добавьте между нимиignore(...)или используйтеstd::ws. На странице про ввод и вывод показана та же проблема сstd::cinи то, как восстановиться, когда чтение числа не удалось. - Вызывать
getline(std::cin, number)сint.getlineчитает только вstd::string. Прочитайте строку, а затем преобразуйте её черезstd::stoiилиstd::istringstream. - Цикл по
eof(). Проверяйте сам вызовgetline:while (std::getline(input, line)). - Забыть
#include <string>.std::getlineдля строк объявлен в<string>. Некоторые компиляторы пропускают его через<iostream>, и тогда код не собирается на другом компиляторе. - Не проверять, открылся ли файл. Из-за неверного пути
std::ifstreamмолча не открывается, и цикл чтения выполняется ноль раз.
Часто задаваемые вопросы
Что делает getline в C++?
std::getline(stream, text) читает символы из stream в std::string text, пока не встретит перевод строки, а затем убирает этот перевод строки из потока, не сохраняя его. В отличие от cin >> text, он сохраняет пробелы, поэтому читает целую строку, например полное имя или предложение.
Почему getline пропускается после cin >> в C++?
cin >> number прекращает чтение на переводе строки, который вы ввели после числа, и оставляет этот перевод строки в буфере ввода. Следующий getline сразу находит его и возвращает пустую строку. Сначала отбросьте его через std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); или вызовите std::getline(std::cin >> std::ws, text), чтобы пропустить ведущие пробельные символы.
Как использовать getline с разделителем, отличным от перевода строки?
Передайте разделитель третьим аргументом: std::getline(stream, field, ','). Он читает до следующей запятой, сохраняет текст перед ней и отбрасывает саму запятую. Переводы строки при этом считаются обычными символами, поэтому обычно так делают с std::stringstream, построенным из одной строки.
Как прочитать файл построчно в C++?
Откройте его через std::ifstream file("data.txt"); и читайте в цикле while (std::getline(file, line)) { ... }. getline возвращает поток, который превращается в false, когда строки закончились, поэтому цикл останавливается в конце файла.
В чём разница между cin.getline и getline?
std::getline(std::cin, text) это свободная функция, которая читает в std::string, а та растёт под строку любой длины. std::cin.getline(buffer, size) это функция-член, которая читает в массив char фиксированного размера и завершается ошибкой, если строка длиннее size - 1 символов. Используйте std::getline со std::string, если у вас нет причины работать с обычными массивами.