Функція scanf є аналогічною printf, але тільки для вводу. Вона надає багато з тих самих можливостей перетворення в зворотньому, правда, напрямку.
int scanf(char *format, ...)
scanf зчитує знаки зі стандартного вводу, інтерпретуючи їх відповідно до специфікації, вказаній форматом (format), і зберігає результат за допомогою решти аргументів. Аргумент формату описано нижче; решта аргументів, кожен з яких повинен бути покажчиком, вказує на те, де відповідний перетворений ввід потрібно зберегти. Так само як і з printf, цей розділ являється підсумком найкорисніших рис, а не вичерпним описом.
scanf зупиняється, якщо вона вичерпає свій список формату, або коли ввід не співпадає із контрольною специфікацією. Вона повертає як значення число елементів вводу, що співпали і яких було присвоєно. Це можна використати, щоб взнати, скільки об'єктів було знайдено. При завершенні файла повертається EOF; зауважте, що не те саме що й 0, який означає, що наступний символ вводу не співпав із першим описом в ланцюжку форматування. Наступний виклик scanf відновить продовжить пошук, починаючи з місця, де було оброблено останній знак.
Існує також функція sscanf, яка читає свій ввід з ланцюжка замість стандартного вводу:
int sscanf(char *string, char *format, arg1, arg2, ...)
Вона сканує ланцюжок, відповідно до формату format і зберігає отримані значення в arg1,arg2 і так далі. Останні мають бути покажчиками.
Ланцюжок формату, як правило, містить описи перетворення, що використовуються для керування перетворенням вводу. Ланцюжок формату може містити:
- Пробіли і табуляцію, які не ігноруються.
- Звичайні знаки (не %), які повинні співпасти з наступним символом, який не є пробілом, з потоку вводу.
- Описувачі перетворення, що складаються зі знака %, необоб'язкового знака блокування присвоєння *, необов'язкового числа, яке вказує ширину поля, необов'язкових h, l абоL, які вказують ширину адресата та символ перетворення.
Описувач перетворення описує перетворення наступного поля вводу. Звичайно, результат розміщено в змінній, на яку вказує відповідний аргумент. Якщо ж за допомогою * вказано блокування присвоєння, ввідне поле пропускається, присвоєння не відбувається. Ввідним полем вважається ланцюжок знаків, які не являються пробілами; воно продовжиться або до наступного пробілу, або доки ширину поля, якщо вказано, вичерпано. Це означає, що scanf читатиме крізь границі для того, щоб знайти ввід, оскільки символи нового рядка також вважаються пробілами. (Символами-пробілами вважаються пробіл, табуляція, новий рядок, повернення каретки, вертикальна табуляція і зміна сторінки.)
Символ перетворення визначає інтерпретацію ввідного поля. Відповідний аргумент повинен бути покажчиком, як вимагається семантикою «виклику за за значенням» C. Символи перетворень показано в Таблиці 7.2.
Таблиця 7.2 Основні перетворення scanf
|
Символ
| Вводимі дані
| Тип аргументу
|
d
| десяткове ціле
| int *.
|
i
| ціле
| int *. Ціле може бути у вісімковій (з 0попереду) або шістнадцятковій (з 0x або 0X) формі.
|
o
| вісімкокове ціле (із або без попереднього 0)
| int *.
|
u
| беззнакове десяткове ціле
| unsigned int *.
|
x
| шістнадцяткове ціле (із або без попереднього 0x або 0X)
| int *.
|
c
| символи
| char *. Наступні введені знаки (без задання 1) розміщено у вказане місце. Звичайний пробіл пригнічено; щоб прочитати наступний не-пробіл, використайте %1s.
|
s
| символьний ланцюжок (не екрановнаий)
| char *, вказуючи на масив символів достатньо великий для ланцюжка і кінцевого '\0', який буде додано.
|
e,f,g
| число з рухомою точкою з необов'язковим знаком, необов'язковою експонентою
| float *.
|
%
| буквальний %
| присвоєння не відбувається
|
Перед знаками перетворення d, i, o, u та x може стояти h, щоб вказати, що в списку аргументів знаходиться покажчик на коротке ціле (short) а не ціле (int), або l (англійська «л»), щоб вказати на покажчик на довге ціле (long).
Як перший приклад, простенький калькулятор з Розділу 4 можна написати зі scanf, щоб здійснювати перетворення вводу:
#include <stdio.h>
main() /* простий калькулятор */
{
double sum, v;
sum = 0;
while (scanf("%lf", &v) == 1)
printf("\t%.2f\n", sum += v);
return 0;
}
Скажімо, ми хочемо прочитати рядки вводу, які містять дату у формі
25 Dec 1988
Твердження зі scanf у такому разі становитиме
int day, year;
char monthname[20];
scanf("%d %s %d", &day, monthname, &year);
Знак & не використовується зі змінною, що зберігає назву місяця monthname, оскільки назва масиву і так є покажчиком. Буквальні знаки також можуть з'являтися в ланцюжку форматуscanf; вони повинні співпасти з такими самими знаками у вводі. Тож ми могли би читати дати, що мають форму мм/дд/рр за допомогою виразу зі scanf:
int day, month, year;
scanf("%d/%d/%d", &month, &day, &year);
scanf ігнорує пробіли і табуляцію в ланцюжкові формату. Більше того, вона пропускає пропуски і пробіли (пробіли, табуляцію, нові рядки тощо), розглядаючи ввід. Для прочитання вводу, чий формат не є сталим, кращим буде читати по одному рядкові за раз, після чого розбити його на окремі частини за допомогою scanf. Наприклад, скажімо ми хотіли би прочитати рядки, які можуть включати дату в одній з, наведених вище, форм. В такому разі ми могли би написати
while (getline(line, sizeof(line)) > 0) {
if (sscanf(line, "%d %s %d", &day, monthname, &year) == 3)
printf("valid: %s\n", line); /* форма 25 Dec 1988 */
else if (sscanf(line, "%d/%d/%d", &month, &day, &year) == 3)
printf("valid: %s\n", line); /* форма мм/дд/рр */
else
printf("invalid: %s\n", line); /* недійсна форма */
}
Виклики scanf можна змішувати з викликами інших функцій вводу. Наступний виклик будь-якої ввідної функції почнеться з прочитання першого знака, не прочитаного scanf.
Останнє попередження: аргументи scanf і sscanf повинні бу покажчиками. Найчастішою помилкою є написання
scanf("%d", n);
замість
scanf("%d", &n);
Цю помилку, як правило, не буде виявлено під час компіляції.
Вправа 7-4. Напишіть власну версію scanf, аналогічну minprintf з попереднього розділу.
Вправа 5-5. Перепишіть postfix-калькулятор з Розділу 4 так, щоб він використовував scanf і/або sscanf для вводу і перетворення чисел.