Урок 6.4. Ввод – вывод строк
Ввод строки можно осуществлять при помощи функции gets():
gets(<указатель на строку>); |
Функция получает строку от стандартного устройства ввода – клавиатуры. Поскольку строка не имеет заранее заданной длины, функция gets() читает символы до тех пор пока не встретит символ новой строки, который посылается в буфер при нажатии клавиши Ввод.
Функция записывает в строку все символы до символа новой строки (не включая его) и добавляет к ним нуль-символ, который завершает строку. В отличие от функции scanf() пробелы не являются разделителями при вводе, поэтому при помощи функции gets() можно вводить предложения, в которых слова разделены пробелами.
Например:
char name[81]; gets (name); char *s; //необходимо позаботиться, чтобы s указывала на //какую-то определенную область памяти; //выделяем память: s = new char[22]; gets(s); |
Вывод строк можно осуществить при помощи функции puts():
puts(<указатель на строку>); |
Функция последовательно выводит символы, начиная с того, на который указывает <указатель на строку> и заканчивает работу, если встретился нуль-символ.
Например:
char text[] = “Строка символов”; puts(“Вот строка:”); puts(text); |
Далее приведем пример программы иллюстрирующий правильный и безопасный ввод строк.
#include <conio.h> #include <stdio.h> #include <string.h> void main() { //неправильный ввод строк c1 и s1 не инициализированы //т.е. память под строки не выделена, будет выдано сообщение об ОШИБКЕ! char *s1; printf("Enter the text, please:\n"); gets(s1); puts(s1); //правильно, но небезопасно, //пользователь может ввести более 199 символа, //а место есть только для 199 + нуль-символ char s[200]; printf("Enter the text <=199 characters, please:\n"); gets(s); puts(s); //такой же самый пример, но с динамическим //выделением памяти для трех строк char *d[3]; for(int i = 0; i < 3; i++) { d[i] = new char[10]; printf("Enter the text <=9 characters, please:\n"); gets(d[i]); } for(int i = 0; i < 3; i++) { printf("\n"); puts(d[i]); } char m[3][10]; for(int i = 0; i < 3; i++) { printf("Enter the text <=9 characters, please:\n"); gets(m[i]); } for(int i = 0; i < 3; i++) { printf("\n"); puts(m[i]); } //надежный ввод строки функцией fgets() //указывается размер буфера, «лишние» //символы будут отброшены //fgets(buffer, sizeof(buffer), stdin); //stdin – стандартное устройство ввода char sf[10]; fputs("Enter the string : ", stdout); fgets(sf, sizeof(sf), stdin); fputs("String : ", stdout); fputs(sf, stdout); printf("\nКонец примера\n"); getch(); } |
Урок 6.3. Форматный ввод
6.3. Форматный ввод
Для форматного ввода данных применяется функция scanf(), обращение к которой в общем виде можно записать следующим образом:
scanf(<управляющая строка>,<список ввода>); |
где <управляющая строка> является строкой Си, символами которой могут быть:
а) пробелы, символы табуляции и новой строки ("пустые" символы). Если они есть, то они игнорируются;
б) обычные символы (кроме %). Если они есть, то они должны совпадать с очередными символами во входном потоке данных;
в) форматы, начинающиеся с символа %, за которым может быть записан символ запрещения присваивания * , может быть число, задающее ширину поля входного данного, и обязательно должен быть символ преобразования. * <список ввода> состоит, как и для функции printf(), из элементов, отделенных друг от друга запятой.
Однако в качестве элемента ввода указывается адрес переменной, куда должно поступить значение, а не имя переменной. Чтобы получить адрес, нужно к переменной применить операцию взятия адреса & . Например, если name имя переменной, то ее адрес будет &name.
Для массивов имя массива является его адресом. Форматы в управляющей строке аналогичны форматам в строке вывода функции printf(), но имеются некоторые отличия. Формат ввода имеет следующую общую форму записи:
%*<ширина_l_символ_преобразования> |
В форматах управляющей строки используются следующие наиболее часто употребляющиеся символы преобразования:
Таблица. Спецификации формата функции scanf() |
|
Код |
Назначение |
%a,%A | читает значение с плавающей точкой (только C99) |
%c | на входе ожидается одиночный символ |
%d | на входе ожидается десятичное целое |
%i | читает целое в любом формате (десятичное, восьмеричное или шестнадцатеричное) |
%e,%E | на входе ожидается число с плавающей точкой в форме с точкой или с порядком |
%f | читает число с плавающей точкой |
%F | Аналогично коду %f (только С99) |
%g,%G | читает число с плавающей точкой |
%o | читает восьмеричное число |
%s | на входе ожидается строка символов. Соответствующий элемент ввода должен быть адресом массива, способного вместить всю строку, включая нуль-символ \0, который добавляет сама функция; |
%x,%X | читает шестнадцатеричное число |
%p | читает указатель |
%n | принимает целое значение, равное количеству прочитанных до сих пор символов, см. пример с printf() в предыдущей главе |
%u | читает десятичное целое без знака |
%[ ],%[^] | просматривает набор символов на допустимость/недопустимость |
%% | читает знак процента |
Перед символами преобразования u, x, o и d может стоять буква l, указывающая на то, что соответствующий элемент списка ввода является адресом данного целого типа, описанного с модификатором long. Если буква l стоит перед символом преобразования f или e, то соответствующий элемент списка ввода является адресом данного, описанного как long float или long double соответственно.
Если в формате записана <ширина> , то она определяет максимальный размер поля данного. Меньшее число позиций может быть прочитано, если во входном потоке встретится разделитель полей или символ, который нельзя преобразовать в соответствии с указанным форматом.
Если в формате после % указан знак * , то это обозначает, что данное по этому формату будет взято из входного потока, но не будет присвоено соответствующему элементу списка ввода. Отработка функции scanf() влечет за собой переход программы в состояние ожидания до тех пор, пока не будет подготовлена и введена входная строка исходных данных.
Первое поле данного во входной строке преобразуется в соответствии с первым форматом, и полученное значение заносится по адресу первого элемента списка ввода. Следующее поле переводится в соответствии со вторым форматом и т.д.
Поле данного во входной строке определяется как группа символов до (но не включая) символа-разделителя. Символами-разделителями могут быть пробелы, символы табуляции и символы перехода на новую строку; группа символов до такого символа, который не может быть преобразован в соответствии с текущим форматом; группа из n символов, где n – значение ширины поля. Например, обращение:
int r; float x; char city[50]; scanf("%d %f %s", &r,&x,city); //с входной строкой 25 54.32e-1 Кишинев |
присвоит значение 25 переменной r, значение 5.432 переменной x и строку "Кишинев" с добавлением в конце нуль-символа \0 пошлет в массив city. Обратите внимание на то, что для city не указана операция взятия адреса &, так как в Си значением имени массива является адресная константа, определяющая начальный адрес памяти, с которого располагаются элементы массива. Видоизмененное обращение:
scanf("r=%d x=%f city=%s", &r,&x,city);
с непустыми символами в управляющей строке требует входной строки с точно такими же непустыми символами:
r=25 x=54.32e-1 city=Кишинев
Обращение с указанием в форматах ширины поля данного и с применением символа запрещения присваивания:
scanf("%25d %f %*d %2s",&r,&x,city);
для которого подготовлена, например, входная строка
56789 0123 45a72 ,
присвоит переменной r значение 56, переменной x значение 789.0, пропустит данное 0123, так как в формате стоит символ запрещения присваивания, и поместит строку "45" в city. Последующее обращение к вводу приведет к чтению из буфера, начиная с позиции буквы a. Если бы были прочтены все данные из входного буфера, то даже в этом случае в буфере остается признак конца ввода, так как он является разделителем полей.
Этот факт необходимо учитывать в прикладных программах, которые неоднократно запрашивают исходные данные. Для того, чтобы повторный ввод отработал корректно, после функции scanf() рекомендуется записать getchar(), которая "скушает" признак конца ввода.
Соглашения по организации ввода данных с помощью функции scanf() достаточно громоздки, и здесь мы рассмотрели наиболее употребительные случаи. Более детальные подробности о работе функций ввода-вывода можно найти в пользовательской документации по Си.
Пример использования "шаблонов", формат [ ], [^ ].
Формат [ ] позволяет получить строку, содержащую любые символы, указанные в квадратных скобках. Как только на ввод поступает символ, не входящий в указанный набор, считывание данных прекращается. Формат [^ ], наоборот, помещает в строку символы, не входящие в указанный набор, до тех пор пока не встретит любой из указанных.
char str[30]=""; // строке str будет присвоена последовательность символов из цифр, // как только встретится не цифра ввод, будет прекращен scanf("%[0-9]", str); // строке будет присвоена последовательность символов // до любого из указанных знаков препинания, если на клавиатуре // набрать Hello, World! , то str будет присвоено Hello scanf("%[^;:,!?]", str); // можно ввести любые символы, до тех пор, пока не будет // введена b или m scanf("%[^b,^m]", str); |
Урок 6.2. Форматный вывод
6.2. Форматный вывод
Форматный ввод-вывод позволяет передавать программе и выдавать из программы на внешнее устройство символьные, строковые и числовые значения, обеспечивая преобразование данных в процессе ввода-вывода. Для форматного вывода используется функция printf(), обращение к которой, в общем виде можно записать следующим образом:
printf(<строка_вывода>,<список_вывода>); |
где
- <строка_вывода> – это строка, в состав которой входят обычные символы и группы специальных символов, называемых форматами (или спецификациями преобразований);
- <список_вывода> – это перечисление элементов вывода, отделенных друг от друга запятой: <элемент1>,<элемент2>,...,<элементN>. В качестве <элемент> могут быть использованы константы, переменные и выражения.
Строка вывода и список вывода, в свою очередь, должны быть отделены друг от друга запятой, как это видно из общей формы записи функции printf(). Функция printf() печатает строку вывода и значения элементов списка вывода. Если в качестве элемента записана константа, то печатается константа; если переменная, то печатается значение переменной; если выражение, то печатается значение выражения. Чтобы разобраться, как работает функция printf() и какова роль форматов в строке вывода, рассмотрим фрагмент программы:
int x,y; x=8;y=5; printf("Если к %d прибавить %d, то получим %d.",x,y,x+y); |
Что будет напечатано в результате выполнения программы? Во-первых, "разложим" обращение к функции на составные части в соответствии с общей формой записи. Строка:
"Если к %d прибавить %d, то получим %d.",
названная в наших обозначениях строкой вывода, содержит, кроме обычных символов, 3 группы символов, представляющих собой форматы, а именно: %d %d %d , которые обозначают, что в списке вывода имеются 3 элемента, значения которых должны быть напечатаны в форме десятичной целой константы (об этом говорит символ в форматах – d). Во-вторых, список вывода содержит 3 элемента, значения которых нужно напечатать: две переменные и одно выражение: x,y,х+у.
Таким образом, каждому формату из строки вывода соответствует элемент из списка вывода. Функция printf() осуществляет печать строки вывода, и если в ней имеется формат, то вместо i-ого формата "вставляется" в печатаемую строку значение i-ого элемента списка вывода. В результате выполнения этого фрагмента программы будет напечатано:
Если к 8 прибавить 5, то получим 13.
В общем случае форматы определяют, к какому виду должны быть преобразованы значения печатаемых элементов, и поэтому форматы часто называют еще и по-другому – спецификациями преобразования.
Так, например, значение элемента типа int может быть напечатано в виде вещественной константы с точкой или с порядком, либо в виде шестнадцатеричного или восьмеричного представления.
Каким же образом компилятор выделяет форматы из строки вывода? Отличительным признаком формата является символ %, за которым следуют строго определенные символы, задающие формат. В общем случае формат, содержащийся в строке вывода, имеет следующую форму записи:
%-<строка цифр1>.<строка цифр2 l символ преобразования> |
Обязательными составными частями формата должны быть первый символ % и последний символ <символ преобразования>. Остальные компоненты общей формы записи могут быть опущены, как это имеет место в только что рассмотренном примере (формат %d, где d – cимвол преобразования).
Символ преобразования определяет, в каком виде должно быть напечатано выводимое значение. В качестве символа преобразования могут быть использованы символы из таблицы 6.1.
Таблица 6.1. Символы преобразования
Символ преобразо- вания | Вид печатаемого значения |
d, i | Десятичная целая константа |
c | Одиночный символ |
s, S | Строка символов |
e, E | Вещественная константа с порядком |
f, F | Вещественная константа с точкой |
a, A | Вещественная константа в шестнадцатеричном виде |
u | Десятичная целая константа без знака |
o | Восьмеричная целая константа без знака |
х, X | Шестнадцатеричная целая константа без знака |
g, G | Используется вместо f и е, для сокращения записи |
p | Указатель |
n | Указатель на int |
Замечание. n – спецификатор указывающий, что в целочисленной переменной, на которую указывает ассоциированный с данным спецификатором указатель, будет храниться число символов, выведенных к моменту обработки спецификации %n, ничего печататься при этом не будет. Пример смотрите в конце страницы.
Составные части формата, находящиеся между символом % и символом преобразования, называются модификаторами. Влияние модификаторов, если они использованы в формате, на вид печатаемого значения заключается в следующем:
- знак минус указывает на то, что печатаемое значение должно быть "прижато" к левой границе поля вывода. Если минус в формате отсутствует, печатаемое значение прижимается к правой границе поля вывода;
- <строка цифр1> задает минимальную ширину (размер) поля вывода. Большее поле будет использовано, если печатаемое число (или строка) не помещается в заданное поле. Если печатаемое число (или строка) меньше заданной ширины поля вывода, то оно дополняется ведущими пробелами так, чтобы была выдержана заданная ширина поля. Если <строка цифр1> начинается с нуля (этот нуль не означает, что размер восьмеричный), то вместо пробелов добавляются нули;
- знак точка отделяет <строку цифр1> от <строки цифр2>;
- <строка цифр2> определяет разрядность. Для вещественных типов данных – количество печатаемых цифр справа от десятичной точки, для строк – количество печатаемых символов;
- l – маркер длины указывает, что соответствующий элемент данных имеет тип long, а не int.
Рассмотрим, как форматы влияют на вид печатаемых значений.
Пример1. Влияние модификатора ширины поля <строка цифр1> на печать целого числа.
main() { printf("//%d//\n",336); //модификаторы отсутствуют printf("//%2d//\n",336); //ширина поля меньше ширины числа printf("//%10d//\n",336); //ширина поля больше ширины числа printf("//%-10d//\n",336); //число прижато к левой границе поля вывода } |
В строке вывода записаны символы /, чтобы при печати было видно, где начинается и заканчивается каждое поле. Кроме того, в строке вывода имеется управляющий символ \n (новая строка), который обеспечивает для каждой функции printf() печать с новой строки. Результат выполнения программы будет следующим:
/336/ /336/ / 336/ /336 /
Пример 2. Влияние форматов на печать вещественных данных.
main() { printf("//%f//\n",1234.56); //модификаторов нет printf("//%e//\n",1234.56); //модификаторов нет printf("//%4.2f//\n",1234.56); //число не входит в заданную ширину printf("//%3.1f//\n",1234.56); //число не входит в ширину printf("//%10.3f//\n",1234.56); printf("//%10.3e//\n",1234.56); } |
Результаты выполнения:
/1234.560000/
/1.234560е+03/
/1234.56/
/1234.6/
/ 1234.560/
/ 1.234е+03/
В первых двух случаях модификаторы отсутствуют. С шириной поля все ясно – число занимает такой размер поля, который ему требуется, а вот точность по умолчанию равна 6, что обозначает 6 знаков после десятичной точки. Как видим, напечатанное число в формате %f отличается от исходного. Это произошло потому, что на печать выводится 10 цифр, в то время как числа с плавающей точкой в системе, на которой была просчитана программа, изображаются с точностью до 6-7 цифр.
По умолчанию для формата %е в мантиссе печатается одна цифра слева от десятичной точки и шесть справа, а порядок подбирается такой, чтобы обеспечить необходимое значение. Однако лишние нули нет смысла выводить и, чтобы избежать этого, нужно задать точность – число цифр справа от десятичной точки. Последние четыре оператора записаны с модификаторами ширины поля точности. Обратите внимание, что четвертый и шестой операторы производят округление выводимых данных.
Пример 3. Влияние модификаторов на печать строк.
main() { printf("//%2s//\n","Выдающееся достижение!"); printf("//%25s//\n","Выдающееся достижение!"); printf("//%25.5s//\n","Выдающееся достижение!"); printf("//%-25.5s//\n","Выдающееся достижение!"); } //Результат работы программы: /Выдающееся достижение!/ / Выдающееся достижение!/ / Выдаю/ /Выдаю / |
В заключение отметим некоторые особенности использования функции printf() для вывода результатов работы программы.
1. Функция printf() использует строку вывода, для того чтобы определить, сколько будет элементов в списке вывода. Таким образом, i-ому формату строки вывода должен соответствовать i-ый элемент списка вывода. Программа будет выдавать абсурдные результаты, если количество форматов и количество элементов вывода не будет совпадать.
2. Функцию printf() можно использовать для печати каких-либо сообщений. В этом случае в строке вывода не должно быть форматов, а список вывода должен отсутствовать, например: printf("данные:\n");
3. Если в строке вывода имеются управляющие символы (см. 3.1.3.), то они действуют в соответствии с функциональным назначением, осуществляя, например, подачу звукового сигнала, возврат каретки, переход на новую строку и т.д.
4. В случае возникновения ошибки вывода функция возвращает EOF, в противном случае возвращается количество выведенных символов и, следовательно, допустим оператор программы, например, такой: к=printf("%d\n",j);
5. Так как символ % является признаком формата, то для того, чтобы его напечатать, в строке вывода нужно задать \% или %%, например:
int k=25; printf("Ожидается снижение цен на %d\%",k); |
В результате отработки этого фрагмента программы будет напечатано:
Ожидается снижение цен на 25%
Как факт маловероятно, но именно это будет напечатано.
6.Для записи количества выведенных символов на данный момент используется формат n
main() { int i = 0; int *ptri = &i; printf("12345678%n abc",ptri); //по адресу ptri запишется количество //выведенных символов на момент printf("\ni = %d",i); //использования формата %n getch(); } // результат работы программы 12345678 abc i = 8 |
Урок 6.1. Ввод-вывод символа
6.1 Ввод-вывод символа
Для чтения в программе одного символа, поступающего из стандартного входного потока, используется встроенная функция getchar(). Обращение к функции обычно производится в составе оператора-выражения, имеющего следующую форму записи:
<переменная> = getchar(); |
Функция getchar() аргументов не имеет. Она получает очередной символ, поступающий с устройства ввода, и возвращает его значение выполняемой программе. В результате обращения к функции getchar() полученный символ присваивается переменной типа char или int.
Функция возвращает значение EOF, если она встречает во входном потоке символов признак конца файла. В стандартной библиотеке определяется, что символическая константа EOF равна -1. Однако все проверки для определения признака конца файла следует писать в терминах EOF, а не -1, для того, чтобы не быть зависимым от специфического значения, реализуемого тем или иным компилятором.
Для вывода символа в стандартный выходной поток используется функция putchar(). Обращение к функции имеет вид:
putchar(<переменная>); |
Рассмотрим пример использования функций ввода-вывода символа:
#include <stdio.h> main() { int simvol; while ((simvol = getchar()) != EOF) putchar(simvol); } |
В данном примере показано, как с помощью рассмотренных функций обеспечивается посимвольный ввод и вывод данных. Символы, полученные с клавиатуры, отображаются на экране дисплея. Обратите внимание на то, что переменная simvol имеет тип int. Так сделано потому, что значениями переменных типа char являются целые числа без знака в диапазоне от 0 до 255 (ASCII коды символов), но в то же время признак EOF, как уже указывалось, имеет числовое значение –1, что недопустимо для переменной типа char.
Функция getchar() возвращает значение типа int и поэтому в состоянии реагировать на признак конца файла возвратом значения –1. В то же время тип int для переменной simvol не влияет на работу функции putchar() – она выводит на печать символ, код которого является значением аргумента (младший байт двухбайтового поля int).
Ввод данных с клавиатуры и передача их в программу может происходить различными способами. В одном случае вводимый символ немедленно поступает в программу, а в другом – вводимые символы накапливаются в некоторой области памяти, называемой буфером. Нажатие клавиши ВВОД приводит к тому, что символы, собранные в буфере, поступают в программу. Первый способ относится к небуферизованному, или прямому вводу, а второй служит примером буферизованного ввода.
Для буферизованного ввода можно отметить, по меньшей мере, два положительных момента. Во-первых, оказывается, что передачу нескольких символов в виде одного блока можно осуществить гораздо быстрее, чем передавать их последовательно по одному. Во-вторых, если при вводе символов будет допущена ошибка, вы можете воспользоваться корректирующими средствами терминала, чтобы ее исправить, и только после этого передать данные программе, нажав клавишу ввод. Рассматриваемая функция getchar() обеспечивает буферизованный ввод.
Функция открывает входной поток данных и символы, вводимые с клавиатуры, поступают в системный буфер ввода (размер буфера 127 байтов) до тех пор, пока не будет нажата клавиша ввод. После того, как нажата клавиша ввод, функция передает в программу первый символ из буфера, при втором обращении к функции передается второй символ и т.д., пока не будут переданы все символы из буфера.
В вышеприведенном примере обращение к функции getchar() стоит в цикле, и на экране будут дублироваться символы текста, вводимого с клавиатуры. Чтобы прекратить работу программы нужно, ввести признак конца файла – EOF. На клавиатуре это делается одновременным нажатием клавиш Ctrl и Z (Ctrl+Z).
Вместе с тем, существуют различного рода программы, для которых буферизованный ввод неприемлем. Это касается программ, для которых нажатие клавиши обозначает определенную команду. В таких программах целесообразно использовать, например, функцию getch(), которая аналогична функции getchar(), но предназначена для прямого ввода.