Что такое cin.ignore()

Работая с входными потоками C++, вы наверняка сталкивались с неожиданным поведением входных данных.

Причиной часто являются символы, которые задерживаются в буфере ввода. Здесь и приходится кстати cin.ignore(). Разберем ее функционал и эффективное использование.

Основы cin.ignore()

cin.ignore()  —  это функция, которой из входного потока удаляются символы. Вот ее базовый синтаксис:

cin.ignore(count, delim);

count: максимальное количество игнорируемых символов.

delim: символ-разделитель, на котором остановаются.

Если не указать эти необязательные параметры, при использовании cin.ignore() удалится только один символ. Вот простой пример:

#include <iostream>
#include <limits>

int main() {
int number;
std::cout << "Enter a number: ";
std::cin >> number;

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

std::string line;
std::cout << "Enter a line of text: ";
std::getline(std::cin, line);

std::cout << "Number: " << number << std::endl;
std::cout << "Text: " << line << std::endl;

return 0;
}

Здесь при помощи cin.ignore() буфер ввода очищается после считывания числа и перед считыванием строки текста. Так что никакой символ новой строки в вызов getline() не вмешается.

Зачем нужен cin.ignore()?

Рассмотрим сценарий без cin.ignore():

#include <iostream>
#include <string>

int main() {
int age;
std::string name;

std::cout << "Enter your age: ";
std::cin >> age;

std::cout << "Enter your name: ";
std::getline(std::cin, name);

std::cout << "Age: " << age << ", Name: " << name << std::endl;

return 0;
}

Запустив этот код и введя 25 в age, мы не получим имени, а программа немедленно завершится. Дело в том, что символ новой строки \n, введенный после age, остается в буфере ввода, и эта пустая строка считывается getline() как имя.

Устраняем эту проблему, добавляя cin.ignore() после считывания age:

std::cin >> age;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(std::cin, name);

Типичные сценарии для cin.ignore()

Очистка буфера ввода

Типичнейший сценарий cin.ignore()  —  очистка всего буфера ввода:

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Этой строкой игнорируется максимум символов вплоть до максимально представляемого значения для streamsize, пока не встретится символ новой строки.

Пропуск определенного количества символов

Иногда требуется пропустить известное количество символов:

#include <iostream>

int main() {
std::cout << "Enter a 6-digit code, then your name: ";

// Пропускаем 6-значный код
std::cin.ignore(6);

std::string name;
std::getline(std::cin, name);

std::cout << "Name: " << name << std::endl;

return 0;
}

Здесь пропускаются первые шесть символов кода, затем считывается имя.

Считывание до определенного символа

cin.ignore() применяется также для считывания и удаления ввода до определенного символа:

#include <iostream>
#include <limits>

int main() {
std::cout << "Enter text (end with *): ";

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '*');

std::cout << "Remaining input: ";
std::string remaining;
std::getline(std::cin, remaining);

std::cout << remaining << std::endl;

return 0;
}

Этой программой игнорируется весь ввод до символа *, затем считывается остальной ввод.

Реальный сценарий: cоздание простой системы меню

Вот пример посложнее, где важен cin.ignore(). Создадим простую систему меню для базового диспетчера задач:

#include <iostream>
#include <vector>
#include <limits>

struct Task {
std::string description;
bool completed;
};

void clearInputBuffer() {
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

void addTask(std::vector<Task>& tasks) {
Task newTask;
std::cout << "Enter task description: ";
clearInputBuffer();
std::getline(std::cin, newTask.description);
newTask.completed = false;
tasks.push_back(newTask);
}

void viewTasks(const std::vector<Task>& tasks) {
if (tasks.empty()) {
std::cout << "No tasks.\n";
return;
}
for (size_t i = 0; i < tasks.size(); ++i) {
std::cout << i + 1 << ". " << tasks[i].description
<< (tasks[i].completed ? " (Completed)" : "") << std::endl;
}
}

void markTaskCompleted(std::vector<Task>& tasks) {
viewTasks(tasks);
if (tasks.empty()) return;

int taskNumber;
std::cout << "Enter the number of the task to mark as completed: ";
std::cin >> taskNumber;

if (taskNumber > 0 && static_cast<size_t>(taskNumber) <= tasks.size()) {
tasks[taskNumber - 1].completed = true;
std::cout << "Task marked as completed.\n";
} else {
std::cout << "Invalid task number.\n";
}
clearInputBuffer();
}

int main() {
std::vector<Task> tasks;
int choice;

while (true) {
std::cout << "\n1. Add Task\n2. View Tasks\n3. Mark Task as Completed\n4. Exit\n";
std::cout << "Enter your choice: ";
std::cin >> choice;

switch (choice) {
case 1:
addTask(tasks);
break;
case 2:
viewTasks(tasks);
break;
case 3:
markTaskCompleted(tasks);
break;
case 4:
std::cout << "Goodbye!\n";
return 0;
default:
std::cout << "Invalid choice. Please try again.\n";
}

clearInputBuffer();
}

return 0;
}

Здесь cin.ignore() используется через функцию clearInputBuffer() в нескольких местах:

  1. После считывания пункта меню  —  для удаления всех остальных символов новой строки.
  2. До считывания описания задачи  —  чтобы убедиться в возможности считать строку целиком.
  3. После обозначения задачи как выполненной  —  для очистки всего остального ввода.

Этим обеспечивается плавная работа системы меню, предотвращаются ошибки ввода, обусловленные символами, которые задерживаются в буфере ввода.

Типичные ошибки и как их избежать

  1. Игнорирование слишком многого: будьте осторожны при использовании cin.ignore() с большими значениями. Если проигнорировать больше символов, чем доступно во входном потоке, cin переводится в нежелательное состояние ожидания.
  2. Невключение <limits>: при использовании std::numeric_limits<std::streamsize>::max() не забудьте включить заголовок <limits>.
  3. Злоупотребление cin.ignore(): чревато тем, что код становится сложнее для восприятия и сопровождения. Используйте ее разумно, то есть после операций cin >> и перед вызовами getline().

Производительность

Обычно влияние cin.ignore() на производительность незначительно. Но при работе с очень большими входными данными или там, где важна производительность, учтите:

  1. Использование cin.ignore() для пропуска больших объемов данных медленнее, чем считывание и удаление этих данных.
  2. Если выполняете много операций ввода, отвяжите cin от cout при помощи cin.tie(nullptr), так повысится производительность ввода.

Заключение

Понимание и корректное использование cin.ignore() важно для эффективной обработки ввода на C++. С ней проще управлять буфером ввода, предотвращать неожиданное поведение и создавать надежные интерактивные программы.

Применяя эти приемы и примеры, вы подготовитесь к работе с различными сценариями ввода в проектах на C++.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи ryan: Understanding and Using cin.ignore() in C++

Предыдущая статьяБыстрый поиск фотографий: оптимизированные подходы
Следующая статьяTrendNow: создание новостного Android-приложения с помощью Jetpack Compose. Часть 1