Функция printf()
обеспечивает вывод данных с заданным форматированием. Аналогично, функция scanf()
используется для форматированного ввода,
преобразуя данные согласно определенному формату и записывая их по адресам указанных переменных:
scanf(строка_формата, адреса_переменных);
Передача адресов в scanf()
вместо значений переменных необходима, чтобы обеспечить возможность изменения значений переменных, находящихся
в вызывающей функции. Для этого требуется доступ к адресам памяти.
Спецификации формата данных в строке для scanf()
практически совпадают с теми, которые используются в printf()
. В этом уроке
мы не будем подробно обсуждать все возможности форматированного ввода через scanf()
, но рассмотрим несколько примеров.
Ввод чисел, символов и строк
Пример, как вводить и выводить целое и вещественное числа, символ и строку:
int a;
float b;
char ch, str[30];
scanf("%d%f%c%s", &a, &b, &ch, str);
printf("%d %.3f %c %s\n", a, b, ch, str);
Результат:
45 34.3456y hello
45 34.346 y hello
Во время исполнения программы все данные вводятся в одну строку, разделенные пробелами и другими символами пустого пространства (например, '\n'). Обратите внимание: пробел при чтении символа учитывается как отдельный символ, поэтому символ в примере расположен непосредственно за числом. данные также можно вводить, используя переходы на новую строку.
В строке формата для scanf()
можно вставлять пробелы между спецификациями: %d %f %c %s
. Они не оказывают никакого воздействия.
Эти данные можно вводить и по отдельности:
scanf("%d", &a);
scanf("%f", &b);
scanf("%c", &ch);
scanf("%s", str);
Обратите внимание, что перед str
отсутствует знак амперсанда. Это не ошибка. В дальнейшем вы узнаете, что имя массива уже является адресом
начала массива.
У scanf()
в спецификации формата вещественных чисел не указывается точность представления. Если использовать что-то вроде %.3f или %.10lf,
возникнут проблемы с вводом вещественных чисел. Форматы %lf и %Lf подходят для double и long double соответственно.
Для целых чисел: длинное целое - %ld, короткое целое - %hd. Существуют также форматные спецификаторы для считывания восьмеричных и шестнадцатеричных чисел.
Функция scanf()
возвращает количество успешно считанных элементов. Это позволяет проверить, корректно ли введены данные. Например:
int a;
double b;
char ch, str[30];
ch = scanf("%d %lf %s", &a, &b, str);
if (ch == 3)
printf("%d %.3lf %s\n", a, b, str);
else
printf("Error input\n");
Использование обычных символов
В формате scanf()
можно использовать обычные символы. Соответственно, при вводе необходимо ввести и эти символы:
int a, b, c;
scanf("%d + %d = %d", &a, &b, &c);
printf("Your answer is %d\nThe correct answer is %d\n", c, a+b);
Когда программа запускается, ввод может выглядеть следующим образом: 342+1024 = 1366. Знаки "+" и "=" должны присутствовать между числами, пробелы по периметру значения не имеют:
45 + 839=875
Your answer is 875
The correct answer is 884
Запрет присваивания
При необходимости проигнорировать вводимые данные и не присваивать их переменным, используется символ "*" после знака % в формате. Данные будут считаны, но не сохранены. Это удобно в случаях, когда поступление данных ненадежно:
float arr[3];
int i;
for(i = 0; i < 3; i++)
scanf("%*s %f", &arr[i]);
printf("Sum: %.2f\n", arr[0]+arr[1]+arr[2]);
В этом примере предполагается, что перед каждым числом будет вводиться строка, которую нужно проигнорировать, например:
First: 23.356
Second: 17.285
Third: 32.457
Sum: 73.098
Использование "шаблонов"
В scanf()
предусмотрены форматы, напоминающие шаблоны. Формат […] позволяет захватывать строку, состоящую из указанных символов в
квадратных скобках, считывание завершается при встрече не входящих в набор символов. Формат [^…] действует наоборот.
Пример ниже показывает, как ввод завершается при первой нецифре, и если первый символ не является цифрой, str останется пустой:
char str[30]="";
scanf("%[0-9]", str);
printf("%s\n", str);
В другом случае строка будет содержать все символы до первого из указанных знаков препинания:
scanf("%[^;:,!?]", str);
printf("%s\n", str);
Результат:
Hello, World!
Hello
Некоторые особенности и ограничения функции scanf()
Если введены некорректные данные, исполнение scanf()
прерывается. Например, в следующем коде:scanf("%d%f", &a, &b);
если ввести строку или символ для a
, что невозможно, b
не будет обработана. Более надежным решением может быть:
scanf("%d", &a);
scanf("%f", &b);
Ошибочный ввод a
не должен бы влиять на b
, так как это отдельный вызов scanf()
, но данные остаются в буфере
и будут пытаться считываться при последующих вызовах. Поэтому стоит предусмотреть очистку буфера в случае некорректного ввода:
if (scanf("%d", &a) != 1)
scanf("%*s");
scanf("%f", &b);
Ввод для scanf()
разделяется пробелами. Это может ограничить возможность записать строку с неизвестным количеством пробелов в одну
переменную. Возможно, потребуется применение getchar()
или цикла, считывающего слова и объединяющего их в строку.
Потенциально мощная функция scanf()
также несет ряд трудностей и затрат.
Решение задач
- На прошлой лекции вы разработали программу с функциями для вычисления факториала и последовательности Фибоначчи. Измените ее, чтобы пользователю предлагался выбор между вычислением факториала или элемента Фибоначчи. Далее программа будет запрашивать число для факториала или номер элемента в ряду Фибоначчи.
- Создайте программу, предлагающую пользователю ввести две даты в формате дд.мм.гггг. Дни, месяцы и годы сохраняются в целочисленные переменные. Сообщите, какая из дат наступила ранее.
- Напишите цикл, который продолжает спрашивать данные пока все значения для переменных
scanf()
не будут корректно присвоены. Протестируйте код.