Урок №219. Стани потоку та валідація користувацького вводу

  Юрій  | 

  Оновл. 12 Тра 2021  | 

 51

На цьому уроці ми розглянемо стани потоку та валідацію користувацького вводу в С++, а також нюанси, пов’язані з даними темами.

Стани потоку

Клас 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) — скидає всі поточні флаги стану потоку і встановлює флаг, переданий в якості параметра (state);

   rdstate() — повертає поточні встановлені флаги;

   setstate(state) — встановлює флаг стану, переданий в якості параметра (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).

Рядкова валідація

Давайте виконаємо просту рядкову валідацію, попросивши користувача ввести своє ім’я. Наші обмеження: ім’я може містити тільки букви і пробіли. Якщо користувач введе що-небудь інше, то ввід відхиляється. Перебирати і перевіряти ми будемо кожен символ користувацького вводу:

Зверніть увагу, цей код не ідеальний: користувач може ввести в якості свого імені djfdfjkdjk jaaad fds або взагалі одні пробіли. Ми можемо покращити валідацію, уточнивши наші критерії перевірки: ім’я користувача повинно містити мінімум 1 символ і не більше 1 пробілу.

Тепер розглянемо інший випадок, коли ми просимо користувача ввести свій номер телефону. На відміну від імені користувача, номер телефону має фіксовану довжину. Отже, ми використовуватимемо інший підхід до валідації користувацького вводу. Ми напишемо функцію, яка перевірятиме номер телефону, який ввів користувач, на відповідність заздалегідь визначеному шаблону (такий ось своєрідний аналог регулярним виразам).

Шаблон працюватиме наступним чином:

   # — будь-яка цифра в користувацькому вводі;

   @ — будь-яка буква в користувацькому вводі;

   _ — будь-який пробіл в користувацькому вводі;

   ? — взагалі будь-який символ.

Всі символи користувацького вводу і нашого шаблону повинні точно збігатися.

Отже, якщо ми хочемо, щоб користувацький ввід відповідав шаблону (###) ###-####, то користувач повинен ввести: ( три цифри ), пробіл, три цифри, тире і ще чотири цифри. Якщо щось з цього не співпаде, то користувацький ввід буде відхилений, наприклад:

Використовуючи цю функцію, ми можемо змусити користувача ввести свій номер телефону точно по заданому нами шаблону. Але це не панацея на всі випадки життя.

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

При роботі з числовим вводом очевидним шляхом розвитку подій є використання оператора вилучення для конвертації користувацького вводу в числовий тип. Перевіряючи failbit, ми можемо сказати, ввів користувач число чи ні. Наприклад:

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

Однак, є один нюанс: якщо користувач введе рядок, який починається з цифр, але потім містить букви (наприклад, 53qwerty74), то перші цифри (53) будуть вилучені в nAge, а залишок рядка (qwerty74) залишиться у вхідному потоці, і failbit при цьому НЕ буде встановлений. Це загрожує наявністю сміття у вхідному потоці при наступному вилученні.

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

Числова валідація за допомогою рядка

У вищенаведеному прикладі знадобилося чимало зусиль, щоб отримати одне просте значення! Інший спосіб обробки числового вводу полягає в тому, щоб прочитати користувацький ввід як рядок, обробити його як рядок і, якщо він пройде перевірку, конвертувати (цей рядок) в числовий тип. Наприклад:

Чи буде цей варіант ефективнішим, ніж пряме числове вилучення, залежить від ваших параметрів валідації і обмежень.

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

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (Немає Оцінок)
Loading...

Залишити відповідь

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