Функция scanf() — это программа ввода общего назначения, выполняющая ввод с консоли. Она может читать данные всех встроенных типов и автоматически преобразовывать числа в соответствующий внутренний формат, scanf() во многом выглядит как обратная к printf(). Вот прототип функции scanf():
int scanf(const char *управляющая_строка, ...);
Эта функция возвращает количество тех элементов данных, которым было успешно присвоено значение. В случае ошибки scanf() возвращает EOF, управляющая_строка определяет преобразование считываемых значений при записи их переменные, на которые указывают элементы списка аргументов.
Управляющая строка состоит из символов трех видов:
- спецификаторов преобразования,
- разделителей,
- символов, не являющихся разделителями.
Теперь поговорим о каждом из этих видов.
Спецификаторы преобразования
Каждый спецификатор формата ввода начинается со знака %, причем спецификаторы формата ввода сообщают функции scanf() тип считываемых данных. Перечень этих кодов (т.е. литер-спецификаторов) приведен в табл. 8.3. Спецификаторам преобразования в порядке слева направо ставятся в соответствие элементы списка аргументов. Рассмотрим некоторые примеры.
Код | Значение |
---|---|
%a | Читает значение с плавающей точкой (только С99) |
%c | Читает одиночный символ |
%d | Читает десятичное целое число |
%i | Читает целое число как в десятичном, так и восьмеричном или шестнадцатеричном формате |
%e | Читает число с плавающей точкой |
%f | Читает число с плавающей точкой |
%g | Читает число с плавающей точкой |
%о | Читает восьмеричное число |
%s | Читает строку |
%x | Читает шестнадцатеричное число |
%p | Читает указатель |
%n | Принимает целое значение, равное количеству уже считанных символов |
%u | Читает десятичное целое число без знака |
%[] | Читает набор сканируемых символов |
%% | Читает знак процента |
Ввод чисел
Для чтения целого числа используйте спецификатор преобразования %d или %i. A для чтения числа с плавающей точкой, представленного в стандартном или экспоненциальном виде, используйте спецификатор преобразования %e, %f или %g. (Кроме того, для чтения числа с плавающей точкой стандарт С99 разрешает использовать также спецификатор преобразования %a.)
Функцию scanf() можно использовать для чтения целых значений в восьмеричной или шестнадцатеричной форме, применяя для этого соответственно команды форматирования %o и %x, последняя из которых может быть как на верхнем, так и на нижнем регистре. Когда вводятся шестнадцатеричные числа, то буквы от А до F, представляющие шестнадцатеричные цифры, должны быть на том же самом регистре, что и литера-спецификатор. Следующая программа читает восьмеричное и шестнадцатеричное число:
#include <stdio.h>
int main(void)
{
int i, j;
scanf("%o%x", &i, &j);
printf("%o %x", i, j);
return 0;
}
Функция scanf() прекращает чтение числа тогда, когда встречается первый нечисловой символ.
Ввод целых значений без знака
Для ввода целого значения без знака используйте спецификатор формата %u. Например, операторы
unsigned num;
scanf("%u", &num);
выполняют считывание целого числа без знака и присваивают его переменной num.
Чтение одиночных символов с помощью scanf()
Как уже говорилось в этой главе, одиночные символы можно прочитать с помощью функции getchar() или какой-либо функции, родственной с ней. Для той же цели можно использовать также вызов функции scanf() со спецификатором формата %c. Но, как и большинство реализаций getchar(), функция scanf() при использовании спецификатора преобразования %c обычно будет выполнять построчно буферизованный ввод. В интерактивной среде такая ситуация вызывает определенные трудности.
При чтении одиночного символа символы разделителей читаются так же, как и любой другой символ, хотя при чтении данных других типов разделители интерпретируются как разделители полей. Например, при вводе с входного потока «x y» фрагмент кода
scanf("%c%c%c", &a, &b, &c);
помещает символ x в a, пробел — в b, а символ y — в c.
Чтение строк
Для чтения из входного потока строки можно использовать функцию scanf() со спецификатором преобразования %s. Использование спецификатора преобразования %s заставляет scanf() читать символы до тех пор, пока не встретится какой-либо разделитель. Читаемые символы помещаются в символьный массив, на который указывает соответствующий аргумент, а после введенных символов еще добавляется символ конца строки (’0′). Что касается scanf(), то таким разделителем может быть пробел, разделитель строк, табуляция, вертикальная табуляция или подача страницы. В отличие от gets(), которая читает строку, пока не будет нажата клавиша <ENTER>, scanf() читает строку до тех пор, пока не встретится первый разделитель. Это означает, что scanf() нельзя использовать для чтения строки «это испытание», потому что после пробела процесс чтения прекратится. Чтобы увидеть, как действует спецификатор %s, попробуйте при выполнении этой программы ввести строку «привет всем»:
#include <stdio.h>
int main(void)
{
char str[80];
printf("Введите строку: ");
scanf("%s", str);
printf("Вот Ваша строка: %s", str);
return 0;
}
Программа выведет только часть строки, то есть слово привет.
Ввод адреса
Для ввода какого-либо адреса памяти используйте спецификатор преобразования %p. Этот спецификатор преобразования заставляет функцию scanf() читать адрес в том формате, который определен архитектурой центрального процессора. Например, следующая программа вначале вводит адрес, а затем отображает то, что находится в памяти по этому адресу:
#include <stdio.h>
int main(void)
{
char *p;
printf("Введите адрес: ");
scanf("%p", &p);
printf("По адресу %p находится %c\n", p, *p);
return 0;
}
Спецификатор %n
Спецификатор %n указывает, что scanf() должна поместить количество символов, считанных (до того момента, когда встретился %n) из входного потока в целую переменную, указанную соответствующим аргументом.
Использование набора сканируемых символов
Функция scanf() поддерживает спецификатор формата общего назначения, называемый набором сканируемых символов (scanset). Набор сканируемых символов представляет собой множество символов. Когда scanf() обрабатывает такое множество, то вводит только те символы, которые входят в набор сканируемых символов. Читаемые символы будут помещаться в массив символов, который указан аргументом, соответствующим набору сканируемых символов. Этот набор определяется следующим образом: все те символы, которые предстоит сканировать, помещают в квадратные скобки. Непосредственно перед открывающей квадратной скобкой должен находиться знак %. Например, следующий набор сканируемых символов дает указание scanf() сканировать только символы X, Y и Z:
%[XYZ]
При использовании набора сканируемых символов функция scanf() продолжает читать символы, помещая их в соответствующий массив символов, пока не встретится символ, не входящий в этот набор. При возвращении из scanf() в массиве символов будет находиться строка, состоящая из считанных символов, причем эта строка будет заканчиваться символом конца строки. Чтобы увидеть, как это все работает, запустите следующую программу:
#include <stdio.h>
int main(void)
{
int i;
char str[80], str2[80];
scanf("%d%[abcdefg]%s", &i, str, str2);
printf("%d %s %s", i, str, str2);
return 0;
}
Введите 123abcdtye, а затем нажмите клавишу <ENTER>. После этого программа выведет 123 abed tye. Так как в данном случае ‘t’ не входит в набор сканируемых символов, то scanf() прекратила чтение символов в переменную str сразу после того, как встретился символ ‘t’. Оставшиеся символы были помещены в переменную str2.
Кроме того, можно указать набор сканируемых символов, работающий с точностью до наоборот; тогда первым символом в таком наборе должен быть ^. Этот символ дает указание scanf() принимать любой символ, который не входит в набор сканируемых символов.
В большинстве реализаций для указания диапазона можно использовать дефис. Например, указанный ниже набор сканируемых символов дает функции scanf() указание принимать символы от А до Z:
%[A-Z]
Следует обратить внимание на такой важный момент: набор сканируемых символов чувствителен к регистру букв. Если нужно сканировать буквы и на верхнем, и на нижнем регистре, то их надо указывать отдельно для каждого регистра.
Пропуск лишних разделителей
Разделитель в управляющей строке дает scanf() указание пропустить в потоке ввода один или несколько начальных разделителей. Разделителями являются пробелы, табуляции, вертикальные табуляции, подачи страниц и разделители строк. В сущности, один разделитель в управляющей строке заставляет scanf() читать, но не сохранять любое количество (в том числе и нулевое) разделителей, которые находятся перед первым символом, не являющимся разделителем.
Символы в управляющей строке, не являющиеся разделителями
Если в управляющей строке находится символ, не являющийся разделителем, то функция scanf() прочитает символ из входного потока, проверит, совпадает ли прочитанный символ с указанным в управляющей строке, и в случае совпадения пропустит прочитанный символ. Например, «%d,%d» заставляет scanf() прочитать целое значение, прочитать запятую и пропустить ее (если это была запятая!), а затем прочитать следующее целое значение. Если же указанный символ во входном потоке не будет найден, то scanf() завершится. Когда нужно прочитать и отбросить знак процента, то в управляющей строке следует указать %%.
Функции scanf() необходимо передавать адреса
Для всех переменных, которые должны получить значения с помощью scanf(), должны быть переданы адреса. Это означает, что все аргументы должны быть указателями. Вспомните, что именно так в С создается вызов по ссылке и именно тогда функция может изменить содержимое аргумента. Например, для считывания целого значения в переменную count можно использовать такой вызов функции scanf():
scanf("%d", &count);
Строки будут читаться в символьные массивы, а имя массива без индекса является адресом первого его элемента. Таким образом, чтобы прочитать строку в символьный массив, можно использовать оператор
scanf("%s", str);
В этом случае str является указателем, и потому перед ним не нужно ставить оператор &.
Модификаторы форматат
Как и printf(), функция scanf() дает возможность модифицировать некоторое число своих спецификаторов формата. В спецификаторах формата моно указать модификатор максимальной длины поля. Это целое число, расположенное между % и спецификатором формата; оно ограничивает число символов, считываемых из этого поля. Например, чтобы считывать в переменную str не более 20 символов, пишите
scanf("%20s", str);
Если поток ввода содержит больше 20 символов, то при следующем вызове функций ввода считывание начнется после того места, где оно закончилось при предыдущем вызове. Например, если вы в ответ на вызов scanf() из этого примера введете
ABCDEFGHIJKLMNOPRSTUVWXYZ
то в str из-за спецификатора максимальной ширины поля будет помещено только 20 символов, то есть символы вплоть до Т. Это значит, что оставшиеся символы UVWXYZ пока еще не прочитаны. При следующем вызове scanf(), например при выполнении оператора
scanf("%s", str);
в str будут помешены буквы UVWXYZ. Ввод из поля может завершиться и до того, как будет достигнута максимальная длина поля — если встретится разделитель. В таком случае scanf() переходит к следующему полю.
Чтобы прочитать длинное целое, перед спецификатором формата поместите l (эль). А для чтения короткого целого значения перед спецификатором формата следует поместить n. Эти модификаторы можно использовать со следующими кодами форматов: d, i, o, u, x и n.
По умолчанию спецификаторы f, e и g дают scanf() указание присваивать данные переменной типа float. Если перед одним из этих спецификаторов будет помещен l (эль), то scanf() будет присваивать данные переменной типа double. Использование L дает scanf() указание, чтобы переменная, принимающая данные, имела тип long double.
Если в компиляторе предусмотрена обработка двухбайтовых символов[1], добавленных в язык С Поправкой 1 от 1995 года, то модификатор l можно также использовать с такими кодами формата, как c и s. l непосредственно перед c является признаком указателя на объект типа wchar_t. А l непосредственно перед s — признак указателя на массив элементов типа wchar_t. Кроме того, l также применяется для модификации набора сканируемых символов, чтобы этот набор можно было использовать для двухбайтовых символов.
В Стандарте С99, кроме перечисленных, предусмотрены также модификаторы ll и hh, последний из которых можно применять к спецификаторам d, i, o, u, x или n. Он является признаком того, что соответствующий аргумент является указателем на значение, типа signed или unsigned char. Кроме того, к спецификаторам d, i, o, u, x и n можно применять и ll, этот спецификатор является признаком того, что соответствующий аргумент является указателем на значение типа signed (или unsigned) long long int.
На заметку | В С99 для функции scanf() имеются еще и другие модификаторы типа; о них рассказывается в части II. |
Подавление ввода
scanf() может прочитать поле, но не присваивать прочитанное значение никакой переменной; для этого надо перед литерой-спецификатором формата поля поставить звездочку, *. Например, когда выполняется оператор
scanf("%d%*c%d", &x, &y);
можно ввести пару координат 10,10. Запятая будет прочитана правильно, но ничему не будет присвоена. Подавление присвоения особенно полезно тогда, когда нужно обработать только часть того, что вводится.
[1]Называются также символами в расширенном 16-битном алфавите или символами уникода. (Unicode (уникод) — 16-битовый стандарт кодирования символов, позволяюший представлять алфавиты всех существующих в мире языков.)