Ввод данных в языке C++

Выводить данные на консоль мы научились, теперь рассмотрим объекты и функции, предназначенные для получения данных от пользователя. Для ввода данных в языке C++ предназначен объект cin (console input — ввод с консоли), объявленный в файле iostream. Объект cin позволяет ввести данные любого встроенного типа, например, число, символ или строку. В этом разделе мы рассмотрим лишь основные возможности этого объекта, так как для понимания всех возможностей требуется знание принципов объектно-ориентированного программирования (ООП). В последующих главах мы вернемся к изучению объекта cin.

Прежде чем использовать объект cin необходимо подключить файл iostream с помощью директивы #include:

#include <iostream>

Ввод числа

В качестве примера использования объекта cin произведем суммирование двух целых чисел, введенных пользователем (листинг 2.6).

Листинг 2.6. Суммирование двух введенных чисел

#include <iostream>
#include <clocale>

int main() {
   std::setlocale(LC_ALL, "Russian_Russia.1251");
   int x = 0, y = 0;
   std::cout << "x = ";  // Вывод подсказки
   std::cout.flush();    // Сброс буфера вывода
   std::cin >> x;        // Ввод числа
   std::cout << "y = ";  // Вывод подсказки
   std::cout.flush();    // Сброс буфера вывода
   std::cin >> y;        // Ввод числа
   std::cout << "Сумма равна: " << x + y << std::endl;
   return 0;
}

В первой строке производится подключение заголовочного файла iostream, а во второй — файла clocale. Далее внутри функции main() с помощью функции setlocale() настраивается локаль, а затем объявляются две локальные переменные: x и y. Ключевое слово int в начале строки означает, что объявляются целочисленные переменные. При объявлении переменным сразу присваивается начальное значение (0) с помощью оператора =. Если значение не присвоить, то переменная будет иметь произвольное значение, так называемый «мусор». Как видно из примера, на одной строке можно объявить сразу несколько переменных, разделив их запятыми.

В следующей строке с помощью объекта cout выводится подсказка для пользователя ("x = "). Благодаря этой подсказке пользователь будет знать, что от него требуется. Далее идет вызов метода flush() объекта cin, который сбрасывает данные из буфера потока вывода stdout в консоль. Если буфер вывода принудительно не сбросить, пользователь может не увидеть подсказку вообще.

После ввода числа и нажатия клавиши <Enter> значение будет присвоено переменной x. Ввод значения производится с помощью объекта cin. После названия объекта указывается оператор >>, который как бы указывает направление ввода. При разработке собственных объектов можно перегрузить этот оператор и тем самым управлять вводом. После оператора >> передается название переменной, в которой будут сохранены введенные данные. Так как объекты cout и cin объявлены в пространстве имен std, название пространства имен необходимо указать перед объектами.

Далее производится вывод подсказки, сброс буфера вывода и получение второго числа, которое сохраняется в переменной y. В следующей строке производится сложение двух чисел и вывод результата. Процесс ввода двух чисел и получения суммы выглядит так (данные, вводимые пользователем, выделены полужирным шрифтом):

x = 10
y = 20
Сумма равна: 30

При вводе данных производится попытка преобразования к типу переменной. В нашем примере строка "10" будет преобразована в число 10, а строка "20" — в число 20. Однако, пользователь вместо числа может ввести все что угодно, например, строку не содержащую число. В этом случае преобразование закончится неудачей, переменной не будет присвоено значение, а введенное значение останется в буфере и будет автоматически считано следующей операцией ввода. Проверить отсутствие ошибки при преобразовании можно с помощью метода good() объекта cin. Прототип метода:

bool good() const;

Метод good() возвращает логическое значение true (Истина), которое соответствует числу 1, если ошибок не произошло, или значение false (Ложь), соответствующее числу 0, в противном случае. Вывести сообщение об ошибке и прервать выполнение программы можно так:

std::cout << "x = ";  // Вывод подсказки
std::cout.flush();    // Сброс буфера вывода
std::cin >> x;        // Ввод числа
if (std::cin.good() == false) {
   std::cout << "Error" << std::endl;
   return 1; // Выходим из функции main()
}

В этом примере для проверки условия используется оператор ветвления if. После названия оператора внутри круглых скобок указывается проверяемое выражение. Если выражение возвращает логическое значение true (Истина), то будут выполнены инструкции внутри фигурных скобок. Если выражение возвращает значение false (Ложь), то инструкции внутри фигурных скобок игнорируются и управление передается инструкции, расположенной сразу после закрывающей фигурной скобки. Проверка значения, возвращаемого методом good(), осуществляется с помощью оператора ==. При ошибке метод good() вернет значение false, следовательно, условие false == false будет истинным. В этом случае выводим сообщение об ошибке и выходим из функции main() (и, следовательно, из программы), возвращая значение 1.

Обратите внимание на то, что оператор проверки на равенство содержит два символа =. Указание одного символа = является логической ошибкой, так как этот оператор используется для присваивания значение переменной, а не для проверки условия. Использовать оператор присваивания внутри логического выражения допускается, поэтому компилятор не выведет сообщение об ошибке, однако программа может выполняться некорректно. Подобную ошибку часто допускают начинающие программисты. Например, в следующем примере вместо проверки равенства числу 11, производится операция присваивания:

int x = 10;
if (x = 11) {
   std::cout << "x == 11" << std::endl;
}

Любое число не равное 0 трактуется как логическое значение true, поэтому производится проверка условия true == true, которое является истинным. Проверка соответствия условия значению true производится по умолчанию.

Прерывать выполнение программы в случае ошибки мы научились, теперь рассмотрим возможность обработки ошибки (листинг 2.7). Дадим пользователю три попытки ввода, прежде чем досрочно завершим программу.

Листинг 2.7. Обработка ошибок ввода

#include <iostream>
#include <clocale>
#include <cstdio>

int main() {
   std::setlocale(LC_ALL, "Russian_Russia.1251");
   int x = 0, y = 0, count = 1;
   bool flag = false;
   // Получаем первое число
   do {
      std::cout << "x = ";
      std::cout.flush();
      std::cin >> x;
      if (!std::cin.good()) { // Проверяем успешность ввода
         std::cout << std::endl << "Вы ввели не число" << std::endl;
         ++count;
         if (count > 3) {
            std::cout << "Вы сделали три ошибки" << std::endl;
            return 1;
         }
         std::cin.clear();               // Сбрасываем флаг ошибки
         // Очищаем буфер
         int ch = 0;
         while ((ch = std::cin.get()) != '\n' && ch != EOF);
      }
      else flag = true;
   } while (!flag);
   // Получаем второе число
   flag = false;
   count = 1;
   do {
      std::cout << "y = ";
      std::cout.flush();
      std::cin >> y;
      if (!std::cin.good()) { // Проверяем успешность ввода
         std::cout << std::endl << "Вы ввели не число" << std::endl;
         ++count;
         if (count > 3) {
            std::cout << "Вы сделали три ошибки" << std::endl;
            return 1;
         }
         std::cin.clear();               // Сбрасываем флаг ошибки
         // Очищаем буфер
         int ch = 0;
         while ((ch = std::cin.get()) != '\n' && ch != EOF);
      }
      else flag = true;
   } while (!flag);
   // Выводим результат
   std::cout << "Сумма равна: " << x + y << std::endl;
   return 0;
}

В этом примере внутри функции main() объявляются четыре локальные переменные: x, y, count и flag. Переменные x, y и count являются целочисленными, а переменная flag имеет логический тип bool. Тип bool позволяет хранить в переменной только два значения: true (Истина) и false (Ложь). Значение true соответствует числу 1, а значение false — числу 0. В переменной flag мы будем сохранять текущий статус обработки ошибок, а в переменной count — количество допущенных ошибок подряд.

При возникновении ошибки ее необходимо обработать, а затем вывести повторный запрос на ввод числа. Вполне возможно эту процедуру нужно будет выполнить несколько раз. Причем количество повторов заранее неизвестно. Для выполнения одних и тех же инструкций несколько раз предназначены циклы. В нашем примере используется цикл do...while. Инструкции внутри фигурных скобок будут выполняться до тех пор, пока логическое выражение после ключевого слова while является истинным. Проверка условия производится после выполнения инструкций внутри фигурных скобок, поэтому инструкции выполняются минимум один раз. Обратите внимание на логическое выражение !flag. Восклицательный знак, расположенный перед переменной, инвертирует значение. Например, если переменная содержит значение true, то выражение !true вернет значение false. Так как проверка значения на истинность производится по умолчанию, выражение может не содержать операторов сравнения.

Внутри цикла выводим подсказку пользователю с помощью объекта cout, сбрасываем буфер вывода, а затем получаем значение с помощью объекта cin и сохраняем его в переменной. Далее проверяем наличие ошибки с помощью метода good() объекта cin. Если произошла ошибка, то выводим сообщение Вы ввели не число. Количество попыток мы отслеживаем с помощью переменной count. На начальном этапе она имеет значение 1. Если пользователь не ввел число, то увеличиваем значение на 1 с помощью инструкции:

++count;

Если пользователь допустил три ошибки подряд (переменная count будет содержать значение больше 3), то выводим сообщение Вы сделали три ошибки и выходим из функции main(), вызвав оператор return.

Так как ошибка была обработана необходимо сбросить флаг ошибки. Для этого предназначен метод clear() объекта cin:

void clear(iostate state=goodbit);

В прототипе параметру state присвоено начальное значение, поэтому, если значение по умолчанию устраивает, параметр можно не указывать:

std::cin.clear();

Сбросить флаг ошибки не достаточно, так как ошибочное значение по-прежнему находится в буфере. Если буфер не очистить, то значение будет автоматически передано следующей операции ввода, что породит повторную ошибку. Проигнорировать значение, расположенное в буфере, позволяют следующие инструкции:

int ch = 0;
while ((ch = std::cin.get()) != '\n' && ch != EOF);

Выполнение инструкций в цикле while продолжается до тех пор, пока логическое выражение внутри круглых скобок истинно. Внутри круглых скобок на каждой итерации цикла выполняется получение одного символа из потока ввода с помощью метода get(). Далее производится сравнение полученного символа с символом перевода строки \n и концом потока. Если полученный символ не равен символу перевода строки и не достигнут конец потока, то получаем следующий символ. Цикл выполняется до тех пор, пока не встретится символ перевода строки, который является маркером конца данных в буфере. В итоге буфер ввода будет очищен от ошибочных данных и мы можем запросить данные повторно.

Обычно в цикле while после круглых скобок указываются фигурные скобки, внутри которых размещаются инструкции, выполняемые несколько раз. Если инструкция одна, то фигурные скобки можно не указывать. Нам не нужно вообще выполнять какие-либо инструкции, поэтому мы воспользовались пустой инструкцией, состоящей из точки с запятой.

Очистить буфер можно также с помощью метода ignore() объекта cin. Прототип метода:

istream &ignore(streamsize n=1, int_type delim=EOF);

Первый параметр задает максимальное количество считываемых из буфера символов (по умолчанию один символ). Второй параметр задает символ-разделитель. Если символ-разделитель встретится раньше, чем будет считано указанное количество символов, то считывание прекращается. Метод возвращает ссылку на поток. Пример:

std::cin.ignore(255, '\n');

В этом примере очистка выполняется пока не будет считано 255 символов, либо пока не встретится символ перевода строки \n. Символ перевода строки также удаляется из буфера. Проблема этого способа заключается в том, что мы не знаем сколько символов нужно удалить из буфера. Число 255 — это всего лишь произвольное значение, которого может быть недостаточно. Чтобы полностью очистить буфер ввода можно в первом параметре указать максимальное значение для типа streamsize:

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

После очистки буфера проверяется значение переменной flag. Так как значение переменной является ложным, цикл будет выполнен еще раз.

Если ошибки нет, выполняются инструкции, расположенные после ключевого слова else. В нашем случае переменной flag присваивается значение true. Это значение является условием выхода из цикла.

Далее таким же способом получаем второе число. Так как в предыдущем цикле значения переменных flag и count были изменены, перед циклом производим восстановление первоначальных значений. Иначе выход из цикла произойдет даже в случае ошибки. Если второе число успешно получено, производим вывод суммы чисел.

После объекта cin можно указать сразу несколько переменных, разделив их операторами >>. В этом случае при вводе все значения можно указать на одной строке. Между собой значения должны разделяться пробелами, табуляцией или символом перевода строки. Эти символы не будут присвоены переменным. Например, программу суммирования двух целых чисел можно записать так:

std::setlocale(LC_ALL, "Russian_Russia.1251");
int x = 0, y = 0;
std::cin >> x >> y;
std::cout << "Сумма равна: " << x + y << std::endl;

При вводе строки "10 20" число 10 будет присвоено переменной x, а число 20 — переменной y. Пробел игнорируется.

Учебник C++ (MinGW-W64)
Учебник C++ (MinGW-W64) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов