Menu

getline в C++: чтение целой строки, пропуск ввода и чтение файлов

Как std::getline читает целую строку в std::string: использование с cin, почему после cin >> он возвращает пустую строку, исправления через cin.ignore и std::ws, свои разделители и построчное чтение файла до конца.

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

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

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, если у вас нет причины работать с обычными массивами.

Иллюстрация языков программирования Coddy

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

НАЧАТЬ