Cin ignore c что это
Перейти к содержимому

Cin ignore c что это

Зачем нам вызывать cin.clear () и cin.ignore () после чтения ввода?

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

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

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

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

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

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

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

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

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

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

Что произойдет, если пользователь ответит: «Аркадиуш Влодарчик» на первый вопрос?

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

Вы увидите на консоли «Arkadiusz», но программа не спросит «возраст». Он просто закончится сразу после печати «Аркадиуш».

А «Влодарчик» не показан. Похоже, если бы его не было (?) *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Хорошо, сделаем так! Воспользуемся cin.clear ().

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

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

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

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

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

Ой . Ты только что снял тревогу, а что насчет бревна в воде? * Вернитесь к тексту, где мы говорили о «Влодарчике», как его якобы исчез.

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

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

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

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

«Wlodarczyk» будет удален до того, как начнет шуметь в строке 7.

Что такое 10000 и ‘\ n’?

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

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

Зачем тогда нам нужно «клир»?

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

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

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

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

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

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

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

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

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

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

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

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

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

Если вы укажете возраст «20», вас не спросят имя и фамилию.

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

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

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

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

Когда и зачем мне нужно использовать cin.ignore() в C++?

Я написал очень простую программу на C++, которая просила пользователя ввести число, а затем строку. К моему удивлению, при запуске программа никогда не переставала запрашивать строку. Просто пропустил это. Почитав StackOverflow, я обнаружил, что мне нужно добавить строку, в которой говорилось:

перед строкой, которая получает ввод строки. Добавление этого устранило проблему и заставило программу работать. Мой вопрос: зачем С++ нужна эта cin.ignore() строка и как я могу предсказать, когда мне нужно будет использовать cin.ignore() ?

Вот программа, которую я написал:

Игнор — это именно то, что следует из названия.

Вместо этого он не «выбрасывает» то, что вам не нужно, он игнорирует количество символов, которое вы указываете при его вызове, вплоть до символа, который вы указываете в качестве точки останова.

Он работает как с входными, так и с выходными буферами.

По сути, для std::cin операторов, которые вы используете ignore перед getline вызовом, потому что, когда пользователь вводит что-то с помощью std::cin , он нажимает ввод, и в буфер ‘\n’ попадает символ . cin Затем, если вы используете getline , он получает символ новой строки вместо строки, которую вы хотите. Итак, вы делаете std::cin.ignore(1000,’\n’) и это должно очистить буфер до нужной вам строки. (1000 помещается туда, чтобы пропустить определенное количество символов до указанной точки останова, в данном случае символа новой строки \n.)

Вы думаете об этом неправильно. Вы думаете логическими шагами каждый раз , когда cin или getline используется. Бывший. Сначала спросите номер, затем спросите имя. Это неправильный способ думать о cin . Таким образом, вы сталкиваетесь с состоянием гонки, потому что считаете, что поток свободен каждый раз, когда запрашиваете ввод.

Если вы пишете свою программу исключительно для ввода, вы обнаружите проблему:

В приведенном выше вы думаете: «Сначала получите число». Итак, вы вводите 123 , нажимаете ввод, и ваш вывод будет num=123,mystr=» . Это почему? Это потому, что в потоке, который у вас есть 123\n , 123 анализируется num переменная, пока \n она все еще находится в потоке. При чтении документа для getline функции по умолчанию он будет искать до тех пор, пока не встретится istream a . \n В этом примере, поскольку \n он находится в потоке, он выглядит так, как будто он его «пропустил», но он работал правильно.

Чтобы вышеизложенное работало, вам нужно будет ввести 123Hello World правильный вывод num=123,mystr=’Hello World’ . Это, или вы ставите cin.ignore между cin и getline так, чтобы он разбился на логические шаги, которые вы ожидаете.

Вот почему вам нужна ignore команда. Потому что вы думаете об этом логическими шагами, а не в форме потока, поэтому вы сталкиваетесь с состоянием гонки.

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

Вышеизложенное выглядит логическими шагами. Сначала спросите имя, фамилию, затем возраст. Так что если вы John вводили, то Doe вводите, то 19 вводите, приложение отрабатывает каждый логический шаг. Если вы думаете об этом в «потоках», вы можете просто ввести John Doe 19 вопрос «Имя:», и это также сработает и пропустит оставшиеся вопросы. Чтобы описанное выше работало логическими шагами, вам потребуется ignore оставшийся поток для каждого логического перерыва в вопросах.

Просто не забывайте думать о вводе вашей программы как о чтении из «потока», а не логических шагов. Каждый раз, когда вы вызываете cin его, он читается из потока. Это создает довольно глючное приложение, если пользователь вводит неправильный ввод. Например, если вы ввели символ вместо cin >> double ожидаемого, приложение выдаст странный вывод.

Короткий ответ

Почему? Потому что во входном потоке все еще остаются пробелы (возврат каретки, табуляция, пробелы, новая строка).

Когда? Когда вы используете какую-то функцию, которая сама по себе не игнорирует начальные пробелы. Cin по умолчанию игнорирует и удаляет начальные пробелы, но getline сама по себе не игнорирует начальные пробелы.

Теперь подробный ответ.

Все, что вы вводите в консоль, считывается из стандартного потока stdin. Когда вы что-то вводите, скажем, 256 в вашем случае, и нажимаете ввод, содержимое потока становится 256\n . Теперь cin подхватывает 256 и удаляет его из потока, \n оставаясь при этом в потоке. Теперь, когда вы вводите свое имя, скажем Raddicus , новое содержимое потока будет \nRaddicus .

А теперь подвох. Когда вы пытаетесь прочитать строку с помощью getline, если в качестве третьего аргумента не указан разделитель, getline по умолчанию читает до символа новой строки и удаляет символ новой строки из потока. Таким образом, при вызове новой строки getline считывает и отбрасывает \n поток, в результате чего в mystr считывается пустая строка, которая выглядит так, как будто getline пропускается (но это не так), поскольку в потоке уже была новая строка, getline не будет запрашивать ввод как он уже прочитал то, что должен был прочитать.

Как же здесь поможет cin.ignore?

Согласно выдержке из документации игнорирования cplusplus.com —

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

Extracts characters from the input sequence and discards them, until either n characters have been extracted, or one compares equal to delim.

The function also stops extracting characters if the end-of-file is reached. If this is reached prematurely (before either extracting n characters or finding delim), the function sets the eofbit flag.

Итак, cin.ignore(256, ‘\n’); игнорирует первые 256 символов или весь символ, пока не встретит разделитель (здесь \n в вашем случае), в зависимости от того, что наступит раньше (здесь \n — первый символ, поэтому он игнорируется, пока не встретится \n).

Просто для справки: если вы точно не знаете, сколько символов нужно пропустить, и ваша единственная цель — очистить поток, чтобы подготовиться к чтению строки с помощью getline или cin, вы должны использовать cin.ignore(numeric_limits<streamsize>::max(),’\n’) .

Краткое объяснение: он игнорирует символы, равные максимальному размеру потока, или до тех пор, пока не встретится ‘\n’, в зависимости от того, что произойдет раньше.

7.16 – std::cin и обработка недопустимого ввода

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

При написании программ вы всегда должны учитывать, как пользователи (непреднамеренно или наоборот) будут использовать ваши программы некорректно. Хорошо написанная программа будет предвидеть, как пользователи будут использовать ее неправильно, и либо аккуратно обработает эти случаи, либо вообще предотвратит их появление (если это возможно). Программа, которая хорошо обрабатывает случаи ошибок, называется надежной.

В этом уроке мы подробно рассмотрим способы, которыми пользователь может вводить недопустимые текстовые данные через std::cin , и покажем вам несколько разных способов обработки таких случаев.

std::cin , буферы и извлечение

Чтобы обсудить, как std::cin и operator>> могут давать сбой, сначала полезно немного узнать, как они работают.

Когда мы используем operator>> для получения пользовательского ввода и помещения его в переменную, это называется «извлечением». Соответственно, в этом контексте оператор >> называется оператором извлечения.

Когда пользователь вводит данные в ответ на операцию извлечения, эти данные помещаются в буфер внутри std::cin . Буфер (также называемый буфером данных) – это просто часть памяти, отведенная для временного хранения данных, пока они перемещаются из одного места в другое. В этом случае буфер используется для хранения пользовательских входных данных, пока они ожидают извлечения в переменные.

При использовании оператора извлечения происходит следующая процедура:

  • Если во входном буфере уже есть данные, то для извлечения используются они.
  • Если входной буфер не содержит данных, пользователя просят ввести данные для извлечения (так бывает в большинстве случаев). Когда пользователь нажимает Enter , во входной буфер помещается символ '\n' .
  • operator>> извлекает столько данных из входного буфера, сколько может, в переменную (игнорируя любые начальные пробельные символы, такие как пробелы, табуляции или '\n' ).
  • Любые данные, которые не могут быть извлечены, остаются во входном буфере для следующего извлечения.

Извлечение завершается успешно, если из входного буфера извлечен хотя бы один символ. Любые неизвлеченные входные данные остаются во входном буфере для дальнейшего извлечения. Например:

Если пользователь вводит "5a" , 5 будет извлечено, преобразовано в целое число и присвоено переменной x . А "a\n" останется во входном потоке для следующего извлечения.

Извлечение не выполняется, если входные данные не соответствуют типу переменной, в которую они извлекаются. Например:

Если бы пользователь ввел 'b' , извлечение не удалось бы, потому что 'b' не может быть извлечено в переменную типа int .

Проверка ввода

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

Есть три основных способа проверки ввода:

  • встроенный (по мере печати пользователя):
    • прежде всего, не позволить пользователю вводить недопустимые данные;
    • позволить пользователю ввести в строку всё, что он хочет, затем проверить правильность строки и, если она корректна, преобразовать строку в окончательный формат переменной;
    • позволить пользователю вводить всё, что он хочет, позволить std::cin и operator>> попытаться извлечь данные и обработать случаи ошибок.

    Некоторые графические пользовательские интерфейсы и расширенные текстовые интерфейсы позволяют проверять входные данные, когда пользователь их вводит (символ за символом). В общем случае, программист предоставляет функцию проверки, которая принимает входные данные, введенные пользователем, и возвращает true , если входные данные корректны, и false в противном случае. Эта функция вызывается каждый раз, когда пользователь нажимает клавишу. Если функция проверки возвращает истину, клавиша, которую только что нажал пользователь, принимается. Если функция проверки возвращает false , введенный пользователем символ отбрасывается (и не отображается на экране). Используя этот метод, вы можете гарантировать, что любые входные данные, вводимые пользователем, гарантированно будут корректными, потому что любые недопустимые нажатия клавиш обнаруживаются и немедленно отбрасываются. Но, к сожалению, std::cin не поддерживает этот стиль проверки.

    Поскольку строки не имеют никаких ограничений на ввод символов, извлечение гарантированно завершится успешно (хотя помните, что std::cin прекращает извлечение на первом неведущем пробельном символе). После того, как строка введена, программа может проанализировать эту строку, чтобы узнать, корректна она или нет. Однако анализ строк и преобразование вводимых строк в другие типы (например, числа) может быть сложной задачей, поэтому это делается только в редких случаях.

    Чаще всего мы позволяем std::cin и оператору извлечения выполнять эту тяжелую работу. В этом методе мы позволяем пользователю вводить всё, что он хочет, заставляем std::cin и operator>> попытаться извлечь данные и справиться с последствиями, если это не удастся. Это самый простой способ, о котором мы поговорим ниже.

    Пример программы

    Рассмотрим следующую программу-калькулятор, в которой нет обработки ошибок:

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

    Теперь подумайте, где неверный ввод пользователя может нарушить работу этой программы.

    Сначала мы просим пользователя ввести несколько чисел. Что, если он введет что-то, отличающееся от числа (например, 'q' )? В этом случае извлечение не удастся.

    Во-вторых, мы просим пользователя ввести один из четырех возможных символов. Что, если он введет символ, отличный от ожидаемых? Мы сможем извлечь входные данные, но пока не обрабатываем то, что происходит после.

    В-третьих, что, если мы попросим пользователя ввести символ, а он введет строку типа "*q hello" . Хотя мы можем извлечь нужный нам символ '*' , в буфере останутся дополнительные входные данные, которые могут вызвать проблемы в будущем.

    Типы недопустимых входных текстовых данных

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

    1. извлечение входных данных выполняется успешно, но входные данные не имеют смысла для программы (например, ввод 'k' в качестве математического оператора);
    2. извлечение входных данных выполняется успешно, но пользователь вводит дополнительные данные (например, вводя "*q hello" в качестве математического оператора);
    3. ошибка извлечения входных данных (например, попытка ввести 'q' при запросе ввода числа);
    4. извлечение входных данных выполнено успешно, но пользователь выходит за пределы значения числа.

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

    Давайте разберемся в каждом из этих случаев и в том, как их обрабатывать с помощью std::cin .

    Случай ошибки 1: извлечение успешно, но входные данные не имеют смысла

    Это самый простой случай. Рассмотрим следующий вариант выполнения приведенной выше программы:

    В этом случае мы попросили пользователя ввести один из четырех символов, но вместо этого он ввел 'k' . 'k' – допустимый символ, поэтому std::cin успешно извлекает его в переменную op , и она возвращается в main . Но наша программа не ожидала этого, поэтому она не обрабатывает этот случай правильно (и, таким образом, ничего не выводит).

    Решение здесь простое: выполните проверку ввода. Обычно она состоит из 3 шагов:

    1. убедитесь, что пользовательский ввод соответствует вашим ожиданиям;
    2. если да, верните значение вызывающей функции;
    3. если нет, сообщите пользователю, что что-то пошло не так, и попросите его повторить попытку.

    Вот обновленная функция getOperator() , которая выполняет проверку ввода.

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

    Случай ошибки 2: извлечение успешно, но с посторонними входными данными

    Рассмотрим следующий вариант выполнения приведенной выше программы:

    Как думаете, что будет дальше?

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

    Когда пользователь вводит "5*7" в качестве вводных данных, эти данные попадают в буфер. Затем оператор >> извлекает 5 в переменную x , оставляя в буфере "*7\n" . Затем программа напечатает "Enter one of the following: +, -, *, or /:" . Однако когда был вызван оператор извлечения, он видит символы "*7\n" , ожидающие извлечения в буфере, поэтому он использует их вместо того, чтобы запрашивать у пользователя дополнительные данные. Следовательно, он извлекает символ '*' , оставляя в буфере "7\n" .

    После запроса пользователя ввести другое значение double , из буфера извлекается 7 без ожидания ввода пользователя. Поскольку у пользователя не было возможности ввести дополнительные данные и нажать Enter (добавляя символ новой строки), все запросы в выводе идут вместе в одной строке, даже если вывод правильный.

    Хотя программа работает, выполнение запутано. Было бы лучше, если бы любые введенные посторонние символы просто игнорировались. К счастью, символы игнорировать легко:

    Этот вызов удалит до 100 символов, но если пользователь ввел более 100 символов, мы снова получим беспорядочный вывод. Чтобы игнорировать все символы до следующего символа '\n' , мы можем передать std::numeric_limits<std::streamsize>::max() в std::cin.ignore() . std::numeric_limits<std::streamsize>::max() возвращает наибольшее значение, которое может быть сохранено в переменной типа std::streamsize . Передача этого значения в std::cin.ignore() приводит к отключению проверки счетчика.

    Чтобы игнорировать всё, вплоть до следующего символа '\n' , мы вызываем

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

    Поскольку последний введенный пользователем символ должен быть '\n' , мы можем указать std::cin игнорировать символы в буфере, пока не найдет символ новой строки (который также будет удален).

    Давайте обновим нашу функцию getDouble() , чтобы игнорировать любой посторонний ввод:

    Теперь наша программа будет работать, как ожидалось, даже если мы введем "5*7" при первом запросе ввода – 5 будет извлечено, а остальные символы из входного буфера будут удалены. Поскольку входной буфер теперь пуст, при следующем выполнении операции извлечения данные у пользователя будут запрашиваться правильно!

    Случай ошибки 3: сбой при извлечении

    Теперь рассмотрим следующий вариант выполнения нашей программы калькулятора:

    Неудивительно, что программа работает не так, как ожидалось, но интересно, как она дает сбой:

    и программа внезапно завершается.

    Это очень похоже на случай ввода посторонних символов, но немного отличается. Давайте посмотрим подробнее.

    Когда пользователь вводит 'a' , этот символ помещается в буфер. Затем оператор >> пытается извлечь 'a' в переменную x , которая имеет тип double . Поскольку 'a' нельзя преобразовать в double , оператор >> не может выполнить извлечение. В этот момент происходят две вещи: 'a' остается в буфере, а std::cin переходит в «режим отказа».

    Находясь в «режиме отказа», будущие запросы на извлечение входных данных будут автоматически завершаться ошибкой. Таким образом, в нашей программе калькулятора вывод запросов всё еще печатается, но любые запросы на дальнейшее извлечение игнорируются. Программа просто выполняется до конца, а затем завершается (без вывода результата потому, что мы не прочитали допустимую математическую операцию).

    К счастью, мы можем определить, завершилось ли извлечение сбоем, и исправить это:

    Давайте, интегрируем это в нашу функцию getDouble() :

    Примечание. До C++11 неудачное извлечение не приводило к изменению извлекаемой переменной. Это означает, что если переменная была неинициализирована, она останется неинициализированной в случае неудачного извлечения. Однако, начиная с C++11, неудачное извлечение из-за недопустимого ввода приведет к тому, что переменная будет инициализирована нулем. Инициализация нулем означает, что для переменной установлено значение 0, 0.0, "" или любое другое значение, в которое 0 преобразуется для этого типа.

    Случай ошибки 4: извлечение успешно, но пользователь выходит за пределы значения числа

    Рассмотрим следующий простой пример:

    Что произойдет, если пользователь введет слишком большое число (например, 40000)?

    В приведенном выше случае std::cin немедленно переходит в «режим отказа», но также присваивает переменной ближайшее значение в диапазоне. Следовательно, x остается с присвоенным значением 32767. Дополнительные входные данные пропускаются, оставляя y с инициализированным значением 0. Мы можем обрабатывать этот вид ошибки так же, как и неудачное извлечение.

    Примечание. До C++11 неудачное извлечение не приводило к изменению извлекаемой переменной. Это означает, что если переменная была неинициализирована, в случае неудачного извлечения она останется неинициализированной. Однако, начиная с C++11, неудачное извлечение вне диапазона приведет к тому, что переменной будет присвоено ближайшее значение в диапазоне.

    Собираем всё вместе

    Вот наш пример калькулятора с полной проверкой ошибок:

    Заключение

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

    • Могло ли извлечение закончиться неудачей?
    • Может ли пользователь ввести больше, чем ожидалось?
    • Может ли пользователь ввести бессмысленные входные данные?
    • Может ли пользователь переполнить входные данные?

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

    Следующий код очистит любые посторонние входные данные:

    Следующий код будет проверять и исправлять неудачные извлечения или переполнение:

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

    Примечание автора

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *