Почему мы будем вызывать cin.clear () и cin.ignore () после чтения ввода? - PullRequest
69 голосов
/ 27 февраля 2011

Обучающий курс по C ++ для Университета Google Code , который имел этот код:

// Description: Illustrate the use of cin to get input
// and how to recover from errors.

#include <iostream>
using namespace std;

int main()
{
  int input_var = 0;
  // Enter the do while loop and stay there until either
  // a non-numeric is entered, or -1 is entered.  Note that
  // cin will accept any integer, 4, 40, 400, etc.
  do {
    cout << "Enter a number (-1 = quit): ";
    // The following line accepts input from the keyboard into
    // variable input_var.
    // cin returns false if an input operation fails, that is, if
    // something other than an int (the type of input_var) is entered.
    if (!(cin >> input_var)) {
      cout << "Please enter numbers only." << endl;
      cin.clear();
      cin.ignore(10000,'\n');
    }
    if (input_var != -1) {
      cout << "You entered " << input_var << endl;
    }
  }
  while (input_var != -1);
  cout << "All done." << endl;

  return 0;
}

Каково значение cin.clear() и cin.ignore()? Зачем нужны параметры 10000 и \n?

Ответы [ 4 ]

86 голосов
/ 27 февраля 2011

cin.clear() сбрасывает флаг ошибки на cin (чтобы будущие операции ввода-вывода работали правильно), а затем cin.ignore(10000, '\n') переходит на следующую новую строку (чтобы игнорировать все остальное в той же строке, что и не -номер, чтобы он не вызывал очередной сбой разбора). Он пропустит только до 10000 символов, поэтому в коде предполагается, что пользователь не введет очень длинную недопустимую строку.

31 голосов
/ 27 февраля 2011

Вы вводите

if (!(cin >> input_var))

оператор, если при входе из cin возникает ошибка. Если возникает ошибка, то устанавливается флаг ошибки, и будущие попытки получить ввод потерпят неудачу. Вот почему вам нужно

cin.clear();

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

cin.ignore(10000,'\n');

Он забирает 10000 символов из буфера, но останавливается, если встречает символ новой строки (\ n). 10000 - это просто общее большое значение.

5 голосов
/ 26 октября 2018

Почему мы используем:

1) cin.ignore

2) cin.clear

Просто:

1) Игнорировать (извлекать и отбрасывать) значения, которые нам не нужны в потоке

2) Очистить внутреннее состояние потока. После использования cin.clear внутреннее состояние снова возвращается в Goodbit, что означает отсутствие «ошибок».

Длинная версия:

Если что-то помещено в «поток» (cin), то это должно быть взято оттуда. Под «взятым» мы подразумеваем «использованный», «удаленный», «извлеченный» из потока. Поток имеет поток. Данные текут по кину как вода по течению. Вы просто не можете остановить поток воды;)

Посмотрите на пример:

string name; //line 1
cout << "Give me your name and surname:"<<endl;//line 2
cin >> name;//line 3
int age;//line 4
cout << "Give me your age:" <<endl;//line 5
cin >> age;//line 6

Что произойдет, если пользователь ответит: «Arkadiusz Wlodarczyk» за первый вопрос?

Запустите программу, чтобы убедиться в этом.

Вы увидите на консоли «Arkadiusz», но программа не будет запрашивать у вас «возраст». Это просто закончится сразу же после печати "Arkadiusz".

А "Влодарчик" не показан. Похоже, что если он ушел (?) *

Что случилось? ; -)

Потому что между "Аркадиушем" и "Влодарчиком" есть пробел.

Символ «пробел» между именем и фамилией является для компьютера признаком того, что в потоке «input» ожидают извлечения две переменные.

Компьютер считает, что вы пытаетесь отправить на вход более одной переменной. Этот «космический» знак является знаком для него, чтобы интерпретировать его таким образом.

Таким образом, компьютер назначает «Arkadiusz» для «name» (2), и поскольку вы помещаете более одной строки в поток (вход), компьютер попытается присвоить значение «Wlodarczyk» переменной «age» (!). У пользователя не будет возможности поместить что-либо в 'cin' в строке 6, потому что эта инструкция уже выполнена (!). Зачем? Потому что в потоке еще осталось что-то. И, как я сказал ранее, поток находится в потоке, поэтому все должно быть удалено из него как можно скорее. И такая возможность появилась, когда компьютер увидел инструкцию cin >> age;

Компьютер не знает, что вы создали переменную, в которой хранится возраст кого-либо (строка 4). «возраст» - это просто ярлык. Для компьютера «возраст» можно было бы также назвать: «afsfasgfsagasggas», и это будет то же самое. Для него это просто переменная, которой он попытается присвоить "Wlodarczyk", потому что вы приказали / дали команду компьютеру сделать это в строке (6).

Это неправильно, но эй, это ты сделал это! Это твоя ошибка! Ну, может быть, пользователь, но все же ...


Хорошо, все в порядке. Но как это исправить?!

Давайте попробуем поиграть с этим примером, прежде чем мы исправим его, чтобы выучить еще несколько интересных вещей: -)

Я предпочитаю подходить так, чтобы мы понимали вещи. Исправление чего-либо без знания того, как мы это сделали, не приносит удовлетворения, не правда ли? :)

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate(); //new line is here :-)

После вызова приведенного выше кода вы заметите, что состояние вашего потока (cin) равно 4 (строка 7). Это означает, что его внутреннее состояние больше не равно goodbit. Что-то напутало. Это довольно очевидно, не так ли? Вы пытались присвоить значение типа строки ("Wlodarczyk") переменной типа int 'age'. Типы не совпадают. Пришло время сообщить, что что-то не так. И компьютер делает это, изменяя внутреннее состояние потока. Это как: «Ты, черт возьми, поправь меня, пожалуйста. Я сообщаю тебе« любезно »;-)»

Вы просто не можете больше использовать cin (поток). Он застрял. Как будто вы положили большие деревянные бревна на поток воды. Вы должны исправить это, прежде чем сможете его использовать. Данные (вода) не могут быть получены из этого потока (cin) больше, потому что журнал древесины (внутреннее состояние) не позволяет вам сделать это.

Да, так что, если есть препятствие (деревянные бревна), мы можем просто удалить его, используя специально предназначенные для этого инструменты?

Да!

внутреннее состояние cin, установленное на 4, похоже на сигнал тревоги, который воет и издает шум.

cin.clear очищает состояние до нормального (goodbit).Это как если бы ты пришел и отключил будильник.Вы просто отложите это.Вы знаете, что что-то случилось, поэтому вы говорите: «Можно прекратить шуметь, я знаю, что что-то уже не так, заткнись (очистить)».

Хорошо, давайте сделаем это!Давайте будем использовать cin.clear ().

. Вызвать приведенный ниже код с использованием «Arkadiusz Wlodarczyk» в качестве первого ввода:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl; 
cin.clear(); //new line is here :-)
cout << cin.rdstate()<< endl;  //new line is here :-)

. После выполнения приведенного выше кода мы наверняка увидим, что состояние равно goodbit..

Отлично, так что проблема решена?

Вызвать приведенный ниже код, используя «Arkadiusz Wlodarczyk» в качестве первого ввода:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl;; 
cin.clear(); 
cout << cin.rdstate() << endl; 
cin >> age;//new line is here :-)

Даже если состояние установлено на goodbit послеВ строке 9 пользователя не запрашивается «возраст».Программа останавливается.

ПОЧЕМУ?!

О, чувак ... Вы только что отключили тревогу, как насчет бревна в воде? * Вернитесь к тексту, где мы говорили"Wlodarczyk", как якобы пропал.

Вам нужно удалить "Wlodarczyk" этот кусок дерева из ручья.Отключение будильника не решает проблему вообще.Вы только что заставили его замолчать и думаете, что проблема ушла?;)

Итак, пришло время для другого инструмента:

cin.ignore можно сравнить со специальным грузовиком с веревками, который приходит и удаляет бревна, которые застряли в потоке.Это устраняет проблему, созданную пользователем вашей программы.

Так можем ли мы использовать его даже до срабатывания будильника?

Да:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;

The "Wlodarczyk""будет удален до создания шума в строке 7.

Что такое 10000 и '\ n'?

В нем говорится, что нужно удалить 10000 символов (на всякий случай), пока" \ n "не будетвстретил (ENTER).Кстати, это можно сделать лучше, используя numeric_limits, но это не тема этого ответа.


Таким образом, основная причина проблемы исчезла до того, как был сделан шум ...

Почему мытогда нужно «очистить»?

Что если бы кто-то задал вопрос «дайте мне свой возраст» в строке 6, например: «двадцать лет» вместо того, чтобы писать 20?

Types not 'снова совпадают.Компьютер пытается присвоить строку int.И начинается тревога.У вас нет шансов даже отреагировать на такую ​​ситуацию.cin.ignore не поможет вам в таком случае.

Так что в этом случае мы должны использовать clear:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;
cin.clear();
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

Но следует ли очищать состояние «на всякий случай»?

Конечно, нет.

Если что-то пойдет не так (cin >> age;), инструкция сообщит вам об этом, вернув false.

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

int age;
if (cin >> age) //it's gonna return false if types doesn't match
    cout << "You put integer";
else
    cout << "You bad boy! it was supposed to be int";

Хорошо, мы можем исправить нашу первоначальную проблему, например, такую ​​как:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

int age;
cout << "Give me your age:" << endl;
if (cin >> age)
  cout << "Your age is equal to:" << endl;
else
{
 cin.clear();
 cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
 cout << "Give me your age name as string I dare you";
 cin >> age;
}

Конечно, это можно улучшитьнапример, делая то, что вы делали, используя цикл while.

БОНУС:

Возможно, вам интересно.А что, если я хочу получить имя и фамилию в одной строке от пользователя?Возможно ли использование cin, если cin интерпретирует каждое значение, разделенное пробелом, как другую переменную?

Конечно, вы можете сделать это двумя способами:

1)

string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;

cout << "Hello, " << name << " " << surname << endl;

2) или с помощью функции getline.

getline(cin, nameOfStringVariable);

и вот как это сделать:

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Второй вариант может иметь неприятные последствия, если вы используете его после использования 'cin 'перед getline.

Давайте проверим это:

a)

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endl;

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Если вы введете «20» в качестве возраста, вас не спросят nameAndSurname.

Но если вы сделаете это так:

b)

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endll

все хорошо.

ЧТО?!

Каждый раз, когда вы помещаете что-то на вход (поток), вы оставляете в конце белый символ ENTER ('\ n'). Вы должны каким-то образом вводить значения в консоль.Поэтому это должно произойти, если данные поступают от пользователя.

b) Характеристики cin - это то, что он игнорирует пробел, поэтому, когда вы читаете информацию из cin, символ новой строки '\ n' не имеет значения.Это игнорируется.

a) Функция getline возвращает всю строку до символа новой строки ('\ n'), а когда символ новой строки является первым, функция getline получает '\ n', и это все, что нужно. Вы извлекаете символ новой строки, который был оставлен в потоке пользователем, который поставил «20» в потоке в строке 3.

Так что для исправления это всегда вызывать cin.ignore (); каждый раз, когда вы используете cin для получения любого значения, если вы когда-либо собираетесь использовать getline () внутри вашей программы.

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

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cin.ignore(); // it ignores just enter without arguments being sent. it's same as cin.ignore(1, '\n') 
cout << "Your age is" << age << endl;


string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Надеюсь, вам понятнее потоки.

Ха, пожалуйста, помолчи! : -)

1 голос
/ 20 декабря 2015

используйте cin.ignore(1000,'\n'), чтобы очистить все символы предыдущего cin.get() в буфере, и он остановится, когда встретит '\ n' или 1000 chars сначала.

...