Урок №211. Состояния потока и валидация пользовательского ввода

  Юрий  | 

    | 

  Обновл. 2 Фев 2019  | 

 1694

 ǀ   2 

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

Состояния потока

Класс ios_base содержит следующие флаги для обозначения состояния потоков:

   goodbit – всё хорошо;

   badbit – произошла какая-то фатальная ошибка (например, программа попыталась прочитать данные после конца файла);

   eofbit – поток достиг конца файла;

   failbit – произошла какая-то не фатальная ошибка (например, пользователь ввёл буквы, когда программа ожидала числа).

Хотя эти флаги находятся в ios_base, но, поскольку ios является дочерним классу ios_base, то доступ к этим флагам также возможен и через ios (например, как std::ios::failbit).

ios также предоставляет ряд методов для доступа к состояниям потока выше:

   good() – возвращает true, если установлен goodbit (значит, что с потоком всё ок);

   bad() – возвращает true, если установлен badbit (значит, что произошла какая-то фатальная ошибка);

   eof() – возвращает true, если установлен eofbit (значит, что поток находится в конце файла);

   fail() – возвращает true, если установлен failbit (значит, что произошла какая-то не фатальная ошибка);

   clear() – сбрасывает все текущие флаги состояния потока и задаёт ему goodbit;

   clear(state) – сбрасывает все текущие флаги состояния потока и устанавливает флаг, переданный в качестве параметра;

   rdstate() – возвращает текущие установленные флаги;

   setstate(state) – устанавливает флаг состояния, переданный в качестве параметра.

Чаще всего мы будем иметь дело с failbit, который срабатывает при некорректном пользовательском вводе. Например:

Обратите внимание, эта программа ожидает от пользователя целое число. Однако, если пользователь введёт что-либо другое (например, Tom), то cin не сможет извлечь что-либо в nAge, и для потока будет установлен флаг failbit.

Если же возникает ошибка и для потока задан какой-либо другой флаг, отличный от goodbit, то дальнейшие операции с этим потоком будут проигнорированы. Это можно исправить, вызвав метод clear().

Валидация пользовательского ввода


Валидация пользовательского ввода – это процесс проверки того, соответствует ли пользовательский ввод заданным критериям. Обычно, валидация ввода бывает числовой и строковой.

Со строковой валидацией мы принимаем весь пользовательский ввод в качестве строки, а затем либо принимаем эту строку, либо отклоняем её (в зависимости от критериев проверки). Например, если мы просим пользователя ввести номер телефона, то мы должны убедиться, что этот номер состоит из 10 цифр. В большинстве языков (особенно в скриптовых, таких как Perl и PHP) это можно сделать с помощью регулярных выражений. В C++ нет встроенной поддержки регулярных выражений (возможно, это будет добавлено в следующих версиях C++), поэтому обычно это делается путем проверки каждого символа строки на соответствие заданным критериям.

С числовой валидацией мы обычно заботимся о том, чтобы число, которое ввёл пользователь, находилось в определённом диапазоне (например, от 0 до 20). Однако, в отличие от строковой валидации, пользователь может ввести данные, которые вообще не являются числами – и нам нужно будет обрабатывать и такие случаи.

Для решения этой проблемы C++ предоставляет ряд полезных функций, которые мы можем использовать для определения того, являются ли конкретные символы цифрами или буквами. Следующие функции находятся в заголовочном файле cctype:

   isalnum(int) – возвращает ненулевое значение, если параметром является буква или цифра;

   isalpha(int) – возвращает ненулевое значение, если параметром является буква;

   iscntrl(int) – возвращает ненулевое значение, если параметром является управляющий символ;

   isdigit(int) – возвращает ненулевое значение, если параметром является цифра;

   isgraph(int) – возвращает ненулевое значение, если параметром является выводимый символ, но не пробел;

   isprint(int) – возвращает ненулевое значение, если параметром является выводимый символ, включая пробелы;

   ispunct(int) – возвращает ненулевое значение, если параметром не являются ни буква, ни цифра, ни пробел;

   isspace(int) – возвращает ненулевое значение, если параметром является пробел;

   isxdigit(int) – возвращает ненулевое значение, если параметром является шестнадцатеричная цифра (0-9, a-f, A-F).

Строковая валидация

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

Обратите внимание, этот код не идеален: пользователь может ввести в качестве своего имени djskbvjdb jdhsbj js или вообще одни пробелы. Мы можем усилить валидацию, уточнив наши критерии проверки: имя пользователя должно содержать как минимум 1 символ и не более одного пробела.

Теперь рассмотрим другой случай, где мы просим пользователя ввести свой номер телефона. В отличие от имени пользователя, номер телефона имеет фиксированную длину. Следовательно, мы будем использовать другой подход к валидации пользовательского ввода. Мы напишем функцию, которая будет проверять номер телефона, который ввёл пользователь, на соответствие заранее определённому шаблону (такой вот своеобразный аналог регулярным выражениям).

Шаблон будет работать следующим образом:

   # – любая цифра в пользовательском вводе;

   @ – любая буква в пользовательском вводе;

   _ – любой пробел в пользовательском вводе;

   ? – вообще любой символ.

Все символы пользовательского ввода и нашего шаблона должны точно совпадать.

Итак, если мы хотим, чтобы пользовательский ввод соответствовал шаблону (###) ###-####, то пользователь должен ввести: (, три цифры, ), пробел, три цифры, тире и ещё четыре цифры. Если что-то из этого не совпадает, то пользовательский ввод будет отклонён.

Например:

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

Числовая валидация


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

Например:

Если пользователь ввёл число, то cin.fail() будет false, выполнится оператор break, и мы выйдем из цикла while. Если же пользователь ввёл букву, то cin.fail() будет true, и пользователю снова будет предложено ввести свой возраст.

Однако, есть один нюанс: если пользователь введёт строку, которая начинается с цифр, но затем содержит буквы (например, 53qwerty74), то первые цифры (53) будут извлечены в nAge, а остаток строки (qwerty74) останется во входном потоке и failbit, при этом, НЕ БУДЕТ установлен. Это грозит наличием мусора во входном потоке при следующем извлечении.

Давайте решим эту проблему:

Числовая валидация с помощью строки

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

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

Как вы можете видеть, валидация пользовательского ввода в C++ занимает не так уж и мало времени и усилий. К счастью, многие подобные задачи (например, выполнение числовой валидации с помощью строк) можно легко превратить в функции, которые затем можно будет повторно использовать в других программах.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (12 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 2

  1. Аватар koh:

    ух. еще пару уроков и… все?)

    1. Юрий Юрий:

      Не знаю. Пока что нужно довести эти до конца 🙂

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

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