В данной статье функция scanf() рассматривается в общем виде без привязки к конкретному стандарту, поэтому сюда включены данные из любых стандартов C99, C11, C++11, C++14. Возможно, в некоторых стандартах функция работает с отличиями от изложенного в статье материала.
Функция scanf C - описание
scanf() – это функция, расположенная в заголовочном файле stdio.h(C) и cstdio(C++), она также называется форматированным вводом данных в программу. scanf читает символы из стандартного потока ввода (stdin) и преобразует их в соответствии с форматом, после чего записывает в указанные переменные. Формат – означает, что данные при поступлении приводятся к определенному виду. Таким образом, функция scanf C описывается:
scanf("%формат", &переменная1[, &переменная2,[…]]),
где переменные передаются в виде адресов. Причина такого способа передачи переменных в функцию очевидна: в результате работы она возвращает значение, указывающее на наличие ошибок, поэтому единственным способом изменять значения переменных является передача по адресу. Также, благодаря такому способу, функция может обрабатывать данные любых типов.
Некоторые программисты из-за аналогии с другими языками называют функции, подобные scanf() или printf(), процедурами.
Scanf позволяет осуществлять ввод всех базовых типов языка: char, int, float, string и т.д. В случае с переменными типа string нет нужды указывать знак адреса - «&», так как переменная типа string является массивом, и имя ее является адресом первого элемента массива в памяти компьютера.
Формат ввода данных или управляющая строка
Начнем с рассмотрения примера использования функции scanf C из описания.
#include <stdio.h>
int main()
{
int x;
while (scanf("%d", &x) == 1)
printf("%d\n", x);
return 0; //требование linux-систем
}
Формат ввода состоит из следующих четырех параметров: %[*][ширина][модификаторы] тип. При этом знак «%» и тип являются обязательными параметрами. То есть, минимальный вид формата выглядит следующим образом: “%s”, “%d” и так далее.
В общем случае символы, составляющие строку формата, делятся на:
- спецификаторы формата – все, что начитается с символа %;
- разделительные или пробельные символы - ими считаются пробел, табуляция(\t), новая строка (\n);
- символы, отличающиеся от пробельных.
Функция может оказаться небезопасной.
Используйте вместо scanf() функцию scanf_s().
(сообщение от Visual Studio)
Тип, или спецификаторы формата, или литеры преобразования, или контролирующие символы
Описание scanf C обязано содержать, как минимум, спецификатор формата, который указывается в конце выражений, начинающихся со знака «%». Он сообщает программе тип данных, который следует ожидать при вводе, обычно с клавиатуры. Список всех спецификаторов формата в таблице ниже.
№ | Тип | Значение |
1 | %c | Программа ожидает ввод символа. Переменная для записи должна иметь символьный тип char. |
2 | %d | Программа ожидает ввод десятичного числа целого типа. Переменная должна иметь тип int. |
3 | %i | Программа ожидает ввод десятичного числа целого типа. Переменная должна иметь тип int. |
4 | %e, %E | Программа ожидает ввод числа с плавающей точкой (запятой) в экспоненциальной форме. Переменная должна иметь тип float. |
5 | %f | Программа ожидает ввод числа с плавающей точкой (запятой). Переменная должна иметь тип float. |
6 | %g, %G | Программа ожидает ввод числа с плавающей точкой (запятой). Переменная должна иметь тип float. |
7 | %a | Программа ожидает ввод числа с плавающей точкой (запятой). Переменная должна иметь тип float. |
8 | %o | Программа ожидает ввод восьмеричного числа. Переменная должна иметь тип int. |
9 | %s | Программа ожидает ввод строки. Строкой считается набор любых символов до первого встреченного разделительного символа. Переменная должна иметь тип string. |
10 | %x, %X | Программа ожидает ввод шестнадцатеричного числа. Переменная должна иметь тип int. |
11 | %p | Переменная ожидает ввод указателя. Переменная должна иметь тип указателя. |
12 | %n | Записывает в переменную целое значение, равное количеству считанных до текущего момента символов функцией scanf. |
13 | %u | Программа считывает беззнаковое целое число. Тип переменной должен быть unsigned integer. |
14 | %b | Программа ожидает ввод двоичного числа. Переменная должна иметь тип int. |
15 | %[] | Набор сканируемых символов. Программа ожидает ввод символов, из ограниченного пула, указанного между квадратными скобками. scanf будет работать до тех пор, пока на потоке ввода находятся символы из указанного множества. |
16 | %% | Знак «%». |
Символы в строке формата
Символ звездочка (*)
Звездочка (*) – это флаг, указывающий, что операцию присвоения надо подавить. Звездочка ставится сразу после знака «%». Например,
scanf("%d%*c%d", &x, &y); //игнорировать символ между двумя целыми числами.
scanf("%s%*d%s", str, str2); //игнорировать целое число, между двумя строками.
То есть, если ввести в консоли строку «45-20» программа сделает следующее:
- Переменной «x» будет присвоено значение 45.
- Переменной «y» будет присвоено значение 20.
- А знак минус(тире) «-» будет проигнорирован благодаря «%*c».
Ширина (или ширина поля)
Это целое число между знаком «%» и спецификатором формата, которое определяет максимальное количество символов для считывания за текущую операцию чтения.
scanf("%20s", str); //прочитать первые 20 символов из потока ввода
Следует иметь в виду несколько важных моментов:
- scanf прекратит свою работу, если встретит разделительный символ, даже если не считал 20 символов.
- Если на ввод подается больше 20 символов, в переменную str будут записаны только первые 20 из них.
Модификаторы типа (или точность)
Это специальные флаги, которые модифицируют тип данных, ожидаемых к вводу. Флаг указывается слева от спецификатора типа:
- L или l (маленькая L) При использовании «l» со спецификаторами d, i, o, u, x, флаг сообщает программе, что ожидается ввод данных типа long int. При использовании «l» со спецификатором e или f, флаг сообщает программе, что она должна ожидать ввод значения типа double. Использование «L» сообщает программе, что ожидается значение типа long double. Использование «l» со спецификаторами «c» и «s» сообщает программе, что ожидаются двухбайтовые символы типа wchar_t. Например, "%lc", "%ls", "%l[asd]".
- h – флаг, указывающий на тип short.
- hh – обозначает, что переменная является указателем на значение типа signed char или unsigned char. Флаг можно использовать со спецификаторами d, i, o, u, x, n.
- ll (две маленькие L) – обозначает, что переменная является указателем на значение типа signed long long int или unsigned long long int. Флаг используется со спецификаторами: d, i, o, u, x, n.
- j – обозначает, что переменная является указателем на тип intmax_t или uintmax_t из заголовочного файла stdint.h. Используется со спецификаторами: d, i, o, u, x, n.
- z – обозначает, что переменная является указателем на тип size_t, определение которого находится в stddef.h. Используется со спецификаторами: d, i, o, u, x, n.
- t – обозначает, что переменная является указателем на тип ptrdiff_t. Определение на этот тип находится в stddef.h. Используется со спецификаторами: d, i, o, u, x, n.
Более явно картину с модификаторами можно представить в виде таблицы. Такое описание scanf C для програмистов будет понятнее.
Остальные символы
Любые символы, которые будут встречены в формате, будут отбрасываться. При этом стоит отметить, что наличие в управляющей строке пробельных или разделительных символов (новая строка, пробел, табуляция) может приводить к разному поведению функции. В одной версии scanf() будет читать без сохранения любое количество разделителей до момента, пока не встретит символ, отличный от разделителя, а в другой версии – пробелы (только они) не играют роли и выражение "%d + %d" эквивалентно "%d+%d".
Примеры
Рассмотрим ряд примеров, позволяющих поразмыслить и точнее понять работу функции.
scanf("%3s", str); //если ввести в консоли строку «1d2s3d1;3», в str запишется только «1d2»
scanf("%dminus%d", &x, &y); //символы «minus» между двумя числами будут отброшены
scanf("%5[0-9]", str); //ввод символов в str будет происходить до тех пор, пока их не будет 5 и символы являются числами от 0 до 9.
scanf("%lf", &d); //ожидается ввод данных типа double
scanf("%hd", &x); //ожидается число типа short
scanf("%hu", &y); //ожидается число типа unsigned short
scanf("lx", &z); //ожидается число типа long int
Из приведенных примеров видно, как меняется ожидаемое число с использованием различных символов.
scanf C - описание для начинающих
Данный раздел будет полезен новичкам. Зачастую нужно иметь под рукой не столько полное описание scanf C, сколько детали работы функции.
- Функция является отчасти устаревшей. Существует несколько разных реализаций в библиотеках различных версий. Например, усовершенствованная функция scanf S C, описание которой можно найти на сайте microsoft.
- Количество спецификаторов в формате должно соответствовать количеству переданных функции аргументов.
- Элементы входного потока должны отделяться только разделительными символами: пробел, табуляция, новая строка. Запятая, точка с запятой, точка и т. д. – эти символы не являются разделительными для функции scanf().
- Если scanf встретит разделительный символ, ввод будет остановлен. Если переменных для чтения больше одной, то scanf перейдет к чтению следующей переменной.
- Малейшее несоответствие формата вводимых данных приводит к непредсказуемым результатам работы программы. Хорошо, если программа просто завершится с ошибкой. Но нередко программа продолжает работать и делает это неверно.
- scanf("%20s …", …); Если входной поток превышает 20 символов, то scanf прочитает первые 20 символов и, либо прекратит работу, либо перейдет к чтению следующей переменной, если она указана. При этом следующий вызов scanf продолжит чтение входного потока с того места, где остановилась работа предыдущего вызова scanf. Если при чтении первых 20 символов будет встречен разделительный символ, scanf прекратит свою работу или перейдет к чтению следующей переменной, даже если не считал 20 символов для первой переменной. При этом все несчитанные символы прицепятся к следующей переменной.
- Если набор сканируемых символов начать со знака «^», то scanf будет читать данные до тех пор, пока не встретит разделительный символ или символ из набора. Например, "%[^A-E1-5]" будет считывать данные из потока, пока не будет встречен один из символов английского алфавита от А до Е в верхнем регистре или одно из чисел от 1 до 5.
- Функция scanf C по описанию возвращает число, равное успешному количеству записей в переменные. Если scanf записывает 3 переменные, то результатом успешной работы функции будет возврат числа 3. Если scanf не смог записать ни одной переменной, то результат будет 0. И, наконец, если scanf вообще не смог начать работать по каким-либо причинам, результатом будет EOF.
- Если функция scanf() завершила свою работу некорректно. Например, scanf("%d", &x) – ожидалось число, а на ввод пришли символы. Следующий вызов scanf() начнет свою работу с того места в потоке ввода, где завершился предыдущий вызов функции. Чтобы преодолеть эту проблему, необходимо избавиться от проблемных символов. Это можно сделать, например, вызвав scanf("%*s"). То есть, функция прочитает строку символов и выбросит ее. Таким хитрым образом можно продолжить ввод нужных данных.
- В некоторых реализациях scanf() в наборе сканируемых символов недопустимо использование «-».
- Спецификатор “%c” читает каждый символ из потока. То есть символ -разделитель он также читает. Чтобы пропустить символ разделитель и продолжить читать нужный символ, можно использовать “%1s”.
- При использовании спецификатора «c» допустимо использовать ширину “%10c”, однако тогда в виде переменной функции scanf нужно передать массив элементов типа char.
- “%[a-z]” – это значит "все маленькие буквы английского алфавита", а “%[z-a]” – значит просто 3 символа: ‘z’, ‘a’, ‘-’. Иными словами, символ «-» означает диапазон только в том случае, если стоит между двумя символами, которые находятся в правильном порядке следования. Если «-» находится в конце выражения, в начале или в неверном порядке символов по обеим сторонам от них, то он представляет собой просто символ дефиса, а не диапазон.
Заключение
На этом завершается описание scanf C. Это хорошая удобная функция для работы в небольших программах и при использовании процедурного метода программирования. Однако главным недостатком является количество непредсказуемых ошибок, которые могут возникнуть при использовании scanf. Поэтому, описание scanf C при програмировании лучше всего держать перед глазами. В крупных профессиональных проектах используются потоки iostream, ввиду того, что обладают более высокоуровневыми возможностями, лучше позволяют отлавливать и обрабатывать ошибки, а также работать со значительными объемами информации. Также следует отметить, описание scanf C на русском доступно на сетевых многих источниках, как и примеры ее использования, ввиду возраста функции. Поэтому при необходимости всегда можно найти ответ на тематических форумах.