getline в C++: чтение строки

Привет! В этой статье мы поговорим о функции std::getline из стандартной библиотеки C++. Эта функция позволяет нам прочитать строку из любого потока ввода.

Начнем мы с рассмотрения разных вариантов использования этой функции. Потом посмотрим на ошибки, которые эта функция может вернуть, а закончим упражнениями, которые помогут закрепить изученный материал.

Иллюстрация getline в C++

Чтение строки из потока при помощи getline

getline является частью заголовочного файла <string>. У этой функции есть два варианта:

istream& getline (istream& is, string& str);
istream& getline (istream& is, string& str, char delim);

Эта функция читает поток is до тех пор, пока не встретит разделительный символ. По умолчанию, это символ переноса строки \n, но мы можем указать любой другой передав delim.

Сохраняет прочитанное getline в аргумент str типа std::string, который мы передаем ей в качестве второго аргумента. Все, что хранилось в строке, будет перезаписано прочитанным из потока ввода is.

Возвращает функция поток, из которого была прочитана строка: первый параметр is.

Давайте посмотрим на пример использования функции getline:

#include <iostream>
using namespace std;

int main() {
  string input;

  getline(cin, input);

  cout << "input = \"" << input << "\"" << endl;

  return 0;
}

Вывод программы:

чтение до конца строки
input = "чтение до конца строки"

В этом простом примере мы прочитали из стандартного ввода первую строку. В данном случае это "чтение до конца строки".

Мы также можем поменять символ, до которого мы хотим читать строку:

#include <iostream>
using namespace std;

int main() {
  string input;

  getline(cin, input, ';');

  cout << "input = \"" << input << "\"" << endl;

  return 0;
}
первая часть; вторая часть     
input = "первая часть"

Тут мы добавили третий аргумент - ';'. Поэтому мы получили только то, что было до точки с запятой.

Мы также можем прочитать несколько строк одним вызовом getline:

первая 
часть; 
input = "первая
часть"

Тут getline продолжил чтение, даже после того, как первая строка закончилась, и началась вторая.

В каком случае следующая программа выведет строку “Прочитано успешно”?

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

int main() {
  string input;
  ifstream file("text.txt");
  
  getline(file, input, '*');

  if(input == "test")
    cout << "Прочитано успешно" << endl;
  else
    cout << "Ошибка чтения" << endl;

  return 0;
}

Предполагаем, что файл text.txt существует и может быть прочитан. Каким содержимым должен обладать text.txt?

test*продолжение файла
test;
testing
*test

В примерах выше мы считывали строку из стандартного ввода. Однако getline позволяет нам читать с любого потока. Давайте теперь попробуем считать строку из файла.

Чтение из файла при помощи getline

Поскольку getline может читать из любого потока ввода, давайте воспользуемся классом ifstream для чтения из файла:

#include <fstream>
#include <iostream>
using namespace std;

int main() {
  ifstream shows_file("shows.txt");
  string show;

  getline(shows_file, show);

  cout << "show = \"" << show << "\"" << endl;

  return 0;
}

shows.txt:

Rick and Morty
Stranger Things
Game of Thrones

Вывод программы:

show = "Rick and Morty"

В программе выше мы поменяли первый аргумент функции getline с cin (поток стандартного ввода) на shows_file (поток ввода из файла).

Использование цикла while с getline

Зачем нам останавливаться на первом шоу? Давайте прочитаем все строки, которые есть в файле shows.txt. Для этого можно воспользоваться циклом while:

#include <fstream>
#include <iostream>
using namespace std;

int main() {
  ifstream shows_file("shows.txt");
  string show;

  while (getline(shows_file, show)) {
    cout << "show = \"" << show << "\"" << endl;
  }

  return 0;
}

shows.txt:

Rick and Morty
Stranger Things
Game of Thrones
show = "Rick and Morty"
show = "Stranger Things"
show = "Game of Thrones"

Как вы можете видеть, для чтения всего файла достаточно использовать возвращаемое значение getline в условии цикла while. Так можно сделать, потому что в ifstream реализован оператор конвертации в bool.

При конвертации в bool, ifstream возвращает true если можно продолжить чтение, false если нельзя:

cout << "can read: " << (bool)shows_file << endl;

while (getline(shows_file, show)) {
  cout << "show = \"" << show << "\"" << endl;
}

cout << "can read: " << (bool)shows_file << endl;

Тут мы только добавили конвертацию в bool до и после цикла. Вывод программы:

can read: 1
show = "Rick and Morty"
show = "Stranger Things"
show = "Game of Thrones"
can read: 0

Ожидаемо, мы не можем продолжить чтение после “Game of Thrones” (в файле после этого больше ничего нет).

Ошибки при работе с getline

При работе с потоками ввода всегда могут произойти ошибки. Для того, чтобы понять, что произошло, можно воспользоваться флагами на потоке ввода:

  1. stream.good(): возвращает true если все прошло без ошибок.
  2. stream.eof(): поток ввода закончился.
  3. stream.fail(): произошла ошибка или невозможно интерпретировать прочитанное как строку.
  4. stream.bad(): произошла ошибка.

Давайте добавим проверку на ошибку в наш пример выше:

#include <fstream>
#include <iostream>
using namespace std;

int main() {
  ifstream shows_file("shows.txt");
  string show;

  istream& x = getline(shows_file, show);

  while (getline(shows_file, show)) {
    cout << "show = \"" << show << "\"" << endl;
  }

  if (shows_file.bad()) {
    cerr << "Что-то пошло не так. 😭" << endl;
  }

  return 0;
}

Теперь, если произойдет ошибка при чтении сериалов, мы увидим это в консоли:

show = "Rick and Morty"
show = "Stranger Things"
Что-то пошло не так. 😭

Пример программы с использованием getline

Представьте себе, что вы хотите создать программу для хранения списка задач. Пользователь может вводить свои задачи, и программа будет их отображать. Для этого мы будем использовать std::getline для получения задач от пользователя:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
  vector<string> todo_list;  // Хранение списка задач
  string input;             // Строка для временного хранения пользовательского ввода

  cout << "Добро пожаловать в программу ToDo List!" << endl;
  cout << "Введите ваши задачи. Для завершения ввода введите 'exit'." << endl;

  // Бесконечный цикл для ввода задач до тех пор, пока пользователь не введет 'exit'
  while (true) {
    cout << "Новая задача: ";
    getline(cin, input);  // Получение строки от пользователя

    if (input == "exit") {
      break;  // Завершаем цикл, если пользователь ввел 'exit'
    }

    todo_list.push_back(input);  // Добавляем задачу в список
  }

  cout << "\nВаш список задач:" << endl;
  for (int i = 0; i < todo_list.size(); i++) {
    cout << i + 1 << ". " << todo_list[i] << endl;  // Отображаем задачи пользователя
  }

  return 0;
}

Этот пример иллюстрирует, как можно использовать std::getline для чтения строк из консоли. Вы также можете адаптировать этот код для чтения данных из файлов или других источников ввода.

Упражнения для getline

  1. Простой ввод данных с использованием getline:
    Напишите программу на C++, которая запрашивает у пользователя имя и фамилию. Используйте функцию getline для считывания данных. После этого программа должна выводить приветствие с именем и фамилией пользователя. Например, “Привет, [Имя Фамилия]!“.

  2. Использование getline с разделителем:
    Модифицируйте программу из первого пункта так, чтобы пользователь вводил имя и фамилию через точку с запятой. Ваша задача - считать введенные данные и разделить их на имя и фамилию, используя getline с соответствующим разделителем.

  3. Чтение из файла с использованием getline:
    Создайте текстовый файл students.txt с именами студентов, каждое имя на новой строке. Напишите программу, которая считывает все имена из файла и выводит их на экран в формате: “Студент [номер]: [Имя]“.

Обсуждение