Urok6

Урок 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(), но предназначена для прямого ввода.

Урок 6. ВВОД-ВЫВОД ДАННЫХ

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов