Большинство программ должны взаимодействовать с пользователем посредством ввода определённых данных, будь то ФИО, рост, вес для внесния в базу данных или геометрические размеры какого-то объекта, для которого нужно что-то рассчитать. Все эти данные вводит пользователь - человек, а значит в ответ может придти всё что угодно. Что выдаст программа, если вместо требуемого ей возраста пользователь напишет его словом? Скорее всего программа аварийно завершится или зависнет, но только не в том случае, если в ней предусмотрена "защита от дурака".
Почему программа может завершиться или зависнуть? Программа попытается перевести набор символов в число, что сделать не удастся, а значит дальнейшая работа приложения не определена. Поэтому очень важно организовывать структуру программы так, чтобы при вводе неожиданных для программы данных (некорректных с точки зрения требуемого формата: нужно число, а вводится слово), приложение не "падало", а сообщало пользователю о том, что произошла ошибка и предлагало повторить ввод. Это и есть "защита от дурака".
Реализация защиты от дурака на языке C
Чтобы реализовать хорошую защиту от дурака для ввода различных числовых (int, double...) данных, необходимо считывать не сами числа, а всю вводимую строку и уже потом анализировать ввод. В языке C есть очень хорошая функция sscanf(const char *, const char *, args)
, которая работает аналогично функции scanf(const char *, args)
, возвращая количество успешно считанных аргументов, только чтение данных происходит не из стандартного потока ввода, а из переданной ей первым аргументом строки.
Рассмотрим несколько примеров функций, которые реализуют проверку на дурака, используя функцию sscanf
.
Ввод целого числа с проверкой на некорректный ввод
int get_integer(const char *msg) { char answer[256]; // строка для чтения int n; // итоговое целое число printf("%s", msg); // выводим приглашение ко вводу fgets(answer, sizeof(answer), stdin); // считываем строку // пока не будет считано целое число while (sscanf(answer, "%d", &n) != 1) { printf("Incorrect input. Try again: "); // выводим сообщение об ошибке fgets(answer, sizeof(answer), stdin); // и заново считываем строку } return n; // возвращаем корректное целое число }
Чтобы считать целое число, алгоритм считывает всю строку и затем пытаеся получить из неё целое число. В том случае, если это не удалось, функция выводим сообщение об ошибке с предложением повторить ввод до тех пор, пока не будет введено корректное значение целого числа.
Ввод вещественного числа с проверкой на некорректный ввод
double get_double(const char *msg) { char answer[256]; // строка для чтения double x; // итоговое вещественное число printf("%s", msg); // выводим приглашение ко вводу fgets(answer, sizeof(answer), stdin); // считываем строку // пока не будет считано вещественное число while (sscanf(answer, "%lf", &x) != 1) { printf("Incorrect input. Try again: "); // выводим сообщение об ошибке fgets(answer, sizeof(answer), stdin); // и заново считываем строку } return x; // воозвращаем корректное вещественное число }
Ввод точки на координатной плоскости (структура с двумя вещественными полями)
// описание структуры даных typedef struct point_t { double x; // координата x double y; // координата y } point_t; point_t get_point(const char *msg) { char answer[256]; // строка для чтения point_t point; // итоговая точка printf("%s", msg); // выводим приглашение ко вводу fgets(answer, sizeof(answer), stdin); // считываем строку // пока не будут считаны обе координаты точки while (sscanf(answer, "(%lf,%lf)", &point.x, &point.y) != 2) { printf("Incorrect input. Try again: "); // выводим сообщение об ошибке fgets(answer, sizeof(answer), stdin); // и заново считываем строку } return point; // возвращаем корректную точку }
Как видно из примеров, особенность возвращения функцией sscanf
числа считанных аргументов позволяет контролировать корректность введённых данных в указанном формате, а считывание всей строки защищает от того, что в потоке ввода останутся символы пробела или переноса строки '\n'
, которые уж очень часто заставляют потратить на поиск ошибки ни один час или даже день.