Чтение строк
Функция scanf() может использоваться для чтения строк из потока ввода, для чего надо указать спецификатор формата %s. %s заставляет функцию scanf() читать символы, пока не встретится специальный символ. Прочитанные символы помещаются в массив символов, на который указывает соответствующий аргумент, и результат завершается нулевым символом. Применительно к scanf() специальные символы — это пробел, новая строка, табуляция, вертикальная табуляция или перевод формата. В противоположность gets(), которая читает строку, пока не встретится возврат каретки, функция scanf() читает строку до первого специального символа. Это означает, что нельзя использовать scanf() для чтения строки типа «this is a test», поскольку первый пробел завершит процесс ввода. Для изучения эффекта воздействия спецификатора %s опробуем данную программу, введя строку «hello there»:
#include
int main(void)
char str[80];
printf(«Enter a string: «);
scanf(«%s», str);
printf («Here’s your string: %s», str);
return 0;
>
Программа выдаст только часть «hello» строки.
как читать строку через scanf?
Или заведомо много, или читать посимвольно, или выделять память и читать с помощью scanf_s и смотреть, заполнился ли буфер.
2 ноя 2021 в 14:52
- c
- строки
- ввод
-
Важное на Мете
Связанные
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.1.8.3130
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Форматированный ввод данных в Си — функция scanf
В то время как функция printf() осуществляет форматированный вывод данных, функция scanf() осуществляет их форматированный ввод. Это значит, что поступающие на ввод данные преобразуются соответственно указанному формату(ам) и записываются по адресу(ам) указанной(ых) переменной(ых):
scanf(строка_формата, адреса_переменных);
Причина, по которой в scanf() передаются адреса, а не значения переменных, очевидна. Функция scanf() должна изменять значения переменных тех функций, из которых вызывается. Единственный способ — это получить адреса областей памяти.
Спецификации, допустимые в строке формата, для scanf() почти идентичны тем, что были описаны для функции printf() .
Ввод чисел, символов и строк
Пример ввода-вывода целого и вещественного чисел, символа и строки:
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 отсутствует знак амперсанда. В последующих уроках вы узнаете, что имя массива уже само по себе является ссылкой на массив (другими словами, str содержит адрес начала массива).
В функции scanf() в спецификации формата вещественных чисел не указывается точность представления числа. Запись типа %.3f или %.10lf приведет к невозможности получить вещественное число. Чтобы получить число типа double используют формат %lf , для long double ‒ %Lf .
Для целых чисел: длинное целое ‒ %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\n", c); printf("The correct is %d\n", a+b);
В данном случае, когда программа выполняется, ввод должен выглядеть примерно так: 342+1024 = 1366. Знаки «+» и » lang»>% , но перед буквой формата звездочку * . В таком случае данные считываются, но никакой переменной не присваиваются. Это можно использовать, например, когда нет определенной уверенности в том, что поступит на ввод, с одной стороны, и нужды сохранять эти данные, с другой:
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);
one two three four five! one two three four five
Обратите внимание, что в примере выше в строку были записаны как символы пробелов, так и символ перехода на новую строку. Таким образом, если надо прочитать одну строку вместе с пробелами, можно использовать такой подход:
scanf("%[^'\n']", str);
Здесь в строку считываются все символы, кроме перехода на новую строку. Как только встречается этот символ ‒ ‘\n’ , запись данных в переменную прекращается.
Некоторые особенности и ограничения функции scanf
Как только поступают некорректные данные, функция scanf() завершает свою работу. В примере:
scanf("%d%f", &a, &b);
если переменной a попытаться присвоить символ или строку, что невозможно, то в переменную b потом уже не получится записать число. Можно предположить, что так будет надежнее:
scanf("%d", &a); scanf("%f", &b);
Вроде бы неудачное считывание a не должно оказывать никакого влияния на b , т.к. это уже иной вызов scanf() . Но не все так просто: при некорректном вводе данные остаются в буфере и пытаются «навязать» себя последующим вызовам scanf() . Поэтому при использовании scanf() надо думать о том, как в случае некорректного ввода очистить буфер. Например, это можно сделать так, как показано ниже, или путем использования специальных функций (здесь не рассматриваются):
// если данные не удалось присвоить, if (scanf("%d", &a) != 1) // то выбросить их в виде строки scanf("%*s"); scanf("%f", &b);
- На прошлом занятии вы написали программу, содержащую функции, вычисляющие факториал числа и заданный элемент ряда Фибоначчи. Измените эту программу таким образом, чтобы она запрашивала у пользователя, что он хочет вычислить: факториал или число Фибоначчи. Затем программа запрашивала бы у пользователя либо число для вычисления факториала, либо номер элемента ряда Фибоначчи.
- Напишите программу, которая запрашивает у пользователя две даты в формате дд.мм.гггг. Дни, месяцы и года следует присвоить целочисленным переменным. Программа должна выводить на экран информацию о том, какая дата более ранняя, а какая более поздняя.
- Используя цикл, напишите код, в котором пользователю предлагается вводить данные до тех пор, пока он не сделает это корректно, т.е. пока все указанные в scanf() переменные не получат свои значения. Протестируйте программу.
Курс с решением задач:
pdf-версия
Как ввести строку в си через scanf
Для ввода данных в консоли может использоваться функция scanf() . Эта функция определена в заголовочном файле stdio.h (там же, где и функция printf) и имеет следующее формальное определение:
scanf(форматная_строка, аргументы)
И форматная_строка, и аргументы для функции scanf обязательны.
Форматная_строка содержит спецификации преобразования, которые определяют вводимые данные. Общий вид спецификаций преобразования:
% * ширина_поля модификатор спецификатор
Из этих элементов обязательны только два: знак процента % и спецификатор.
Спецификатор определяет тип вводимых данных:
- %c : считывает один символ
- %d : считывает десятичное целое число
- %i : считывает целое число в любой системе (десятичной, шестнадцатеричной, восьмеричной)
- %u : считывает положительное целое число
- %e : считывает число с плавающей точкой в экспоненциальной форме
- %E : считывает число с плавающей точкой в экспоненциальной форме с заглавным символом экспоненты
- %f : считывает число с плавающей точкой
- %F : считывает число с плавающей точкой
- %g : считывает число с плавающей точкой
- %G : считывает число с плавающей точкой
- %o : считывает восьмеричное число
- %x : считывает шестнадцатеричное число
- %X : считывает шестнадцатеричное число
- %s : считывает строку
- %% : считывает символ процента
Символ звездочки * в спецификации преобразования позволяет пропустить при вводе водимые символы для типа, указанного через спецификатор.
Ширина_поля представляет целое положительное число, которое позволяет определить, какое количество байтов будет учитываться при вводе.
Модификаторы позволяют конкретизировать тип данных. В частности, есть следующие модификаторы:
- h : для ввода значений типа short int ( %hd )
- l : для ввода значений типа long int ( %ld ) или double ( %lf , %le )
- L : для ввода значений типа long double ( %Lf , %Le )
В качестве аргументов в функцию scanf() передаются адреса переменной, которая будет получать введенное значение. Для получения адреса переменной перед ее именем ставится знак амперсанда & . Например, если переменная называется age , то ее адрес мы можем получить с помощью выражения &age .
Например, введем с консоли числовое значение:
#include int main(void)
Здесь вводится значение для переменной age, которая представляет тип int , поэтому в форматную строку в функции scanf передается спецификатор %d . Здесь не используется ни символ звездочки, ни ширина поля, ни модификаторы. Вторым параметром идет адрес переменной age — &age .
После ввода значения мы можем его использовать, например, вывести на консоль:
Input your age:44 age = 44
Аналогичен будет ввод данных других типов:
#include int main(void)
После ввода значения мы можем его использовать, например, вывести на консоль:
Enter the product code: 1234 Enter the price: 234.567 Product code: 1234 price: 234.57
Можно сразу вводить несколько значений. В этом случае в качестве разделителя используется пробел:
#include int main(void)
При вводе данных в консоли функция scanf может использовать пробелы в качестве разделителей, чтобы выдернуть из ввода значения для определенных переменных. Консольный ввод-вывод:
Enter product data: 1234 3 234.56 Product code: 1234 count: 3 price: 234.56
Ввод строк
Функция scanf() также позволяет вводить строки. Например:
#include int main(void) < char name[10]; // вводим имя printf("Enter your name: "); scanf("%10s", name); printf("Name: %s \n",name); return 0; >
Здесь для имени выделяется 10 символов. Теоретически мы можем ввести и большее количество символов, но чтобы только 10 из них учитывались, в строку форматирования передается ширина поля, которая представляет 10 символов — %10s . Когда функция считает достаточное количетсво символов, она прекратит считывание.
Обратите внимание, что для ввода строки перед названием переменной не указывается символ адреса.
scanf("%10s", name);
Потому что название массива уже само по себе представляет адрес на первый элемент массива.
Возможный консольный вывод:
Enter your name: Tom Name: Tom
Однако при использовании этой функции мы можем столкнуться с рядом проблем. Прежде всего попробуйте ввести в предыдущем примере составное имя, в которм подстроки разделены пробелами, например, «Tom Smith».
Enter your name: Tom Smith Name: Tom
Для решения этой проблемы можно использовать один хак:
#include int main(void)
Спецификатор %10[^\n] указавает, что мы по прежнему считываем неболее 10 символов. Квадратные скобки [] представляют позволяют определить набор символов, которые будут извлекаться из ввода или, наоборот, игнорироваться. Так, выражение [^\n] говорит, что надо считать ввод до тех пор пока не встретиться символ перевода строки ‘\n’, то есть пока пользователь не нажмет на клавишу Enter.