Смекни!
smekni.com

Методические указания к выполнению контрольных работ по дисциплине "Основы программирования" (стр. 39 из 40)

f - ожидается число с плавающей точкой; соответствующий аргумент должен быть указателем на переменную типа float.

e - символ преобразования e является синонимом для f. Формат ввода переменной типа float включает необязательный знак, строку цифр, возможно содержащую десятичную точку и необязательное поле экспоненты, состоящее из буквы e, за которой следует целое, возможно, имеющее знак.

Перед символами преобразования d, o и x может стоять l, которая означает, что в списке аргументов должен находиться указатель на переменную типа long, а не типа int. Аналогично, буква l может стоять перед символами преобразования e или f, говоря о том, что в списке аргументов должен находиться указатель на переменную типа double, а не типа float.

Например, обращение:

int i;

float x;

char name[50];

scanf("&d %f %s", &i, &x, name);

со строкой на вводе

25 54.32e-1 thompson

приводит к присваиванию i значения 25, x – значения 5.432 и name – строки "thompson", надлежащим образом законченной символом \0. Эти три поля ввода можно разделить столькими пробелами, табуляциями и символами новых строк, сколько вы пожелаете.

Обращение:

int i;

float x;

char name[50];

scanf("%2d %f %*d %2s", &i, &x, name);

с вводом

56789 0123 45a72

присвоит i значение 56; x будет приравнено 789.0; пропустится 0123; строку "45" поместит в name. При следующем обращении к любой процедуре ввода рассмотрение начнется с буквы a. В этих двух примерах name является указателем и, следовательно, перед ним не нужно помещать знак &.

Пример 9-3. В качестве примера перепишем теперь элементарный калькулятор из главы 5, используя для преобразования ввода функцию scanf:

// Программа-калькулятор

#include <stdio.h>

main()

{

double sum, v;

sum =0;

while (scanf("%lf", &v) != EOF)

printf("&bsol;t%.2f&bsol;n", sum += v);

}

выполнение функции scanf заканчивается либо тогда, когда она исчерпывает свою управляющую строку, либо когда некоторый элемент ввода не совпадает с управляющей спецификацией. В качестве своего значения она возвращает число правильно совпадающих и присвоенных элементов ввода. Это число может быть использовано для определения количества найденных элементов ввода. при выходе на конец файла возвращается EOF; подчеркнем, что это значение отлично от 0, что следующий вводимый символ не удовлетворяет первой спецификации в управляющей строке. При следующем обращении к scanf поиск возобновляется непосредственно за последним введенным символом.

Заключительное предостережение: аргументы функции scanf должны быть указателями. Несомненно, наиболее распространенная ошибка состоит в написании:

scanf("%d", n);

вместо

scanf("%d", &n);

9.5. Форматные преобразования в памяти

От функций scanf и printf происходят функции sscanf и sprintf, которые осуществляют аналогичные преобразования, но оперируют со строкой, а не с файлом. Обращения к этим функциям имеют вид:

sprintf(string, control, arg1, arg2, ...)

sscanf (string, control, arg1, arg2, ...)

Как и раньше, функция sprintf преобразует свои аргументы arg1, arg2 и т.д. В соответствии с форматом, указанным в control, но помещает результаты в string, а не в стандартный вывод. Конечно, строка string должна быть достаточно велика, чтобы принять результат. Например, если name – это символьный массив, а n – целое, то:

sprintf(name, "temp%d", n);

создает в name строку вида tempnnn, где nnn – это значение n.

Функция sscanf выполняет обратные преобразования – она просматривает строку STRING в соответствии с форматом в аргументе control и помещает результирующие значения в аргументы arg1, arg2 и т.д. Эти аргументы должны быть указателями. В результате обращения:

sscanf(name, "temp%d", &n);

переменная n получает значение строки цифр, следующих за temp в name.

Упражнение 8-2. Перепишите настольный калькулятор из главы 5, используя для ввода и преобразования чисел scanf и/или sscanf.

9.6. Доступ к файлам

Все до сих пор написанные программы читали из стандартного ввода и писали в стандартный вывод, относительно которых мы предполагали, что они магическим образом предоставлены программе местной операционной системой.

Следующим шагом в вопросе ввода-вывода является написание программы, работающей с файлом, который не связан заранее с программой. Одной из программ, которая явно демонстрирует потребность в таких операциях, является cat, которая объединяет набор из нескольких именованных файлов в стандартный вывод. Программа cat используется для вывода файлов на терминал и в качестве универсального сборщика ввода для программ, которые не имеют возможности обращаться к файлам по имени. Например, команда:

cat x.cpp y.cpp

печатает содержимое файлов x.cpp и y.cpp в стандартный вывод.

Вопрос состоит в том, как организовать чтение из именованных файлов, т.е. как связать внешние имена, которыми мыслит пользователь, с фактически читающими данные операторами.

Эти правила просты. Прежде чем можно считывать из некоторого файла или записывать в него, этот файл должен быть открыт с помощью функции fopen из стандартной библиотеки. Функция fopen берет внешнее имя (подобное x.cpp или y.cpp), проводит некоторые обслуживающие действия и переговоры с операционной системой (детали которых не должны нас касаться) и возвращает внутреннее имя, которое должно использоваться при последующих чтениях из файла или записях в него.

Это внутреннее имя, называемое «указателем файла», фактически является указателем структуры, которая содержит информацию о файле, такую как место размещения буфера, текущая позиция символа в буфере, происходит ли чтение из файла или запись в него и тому подобное. Пользователи не обязаны знать эти детали, потому что среди определений для стандартного ввода-вывода, получаемых из файла stdio.h, содержится определение структуры с именем file. Единственное необходимое для указателя файла описание демонстрируется примером:

FILE *fopen(), *fp;

Здесь говорится, что fp является указателем на FILE, а fopen возвращает указатель на FILE. Oбратите внимание, что FILE является именем типа, подобным int, а не ярлыку структуры; это реализовано как typedef.


Фактическое обращение к функции fopen в программе имеет вид:

fp=fopen(name,mode);

Первым аргументом функции fopen является «имя» файла, которое задается в виде символьной строки. Второй аргумент mode («режим») также является символьной строкой, которая указывает, как этот файл будет использоваться. Допустимыми режимами являются: «r» – чтение, «w» – запись и «a» – добавление. Если вы откроете файл, который еще не существует, для записи или добавления, то такой файл будет создан (если это возможно). Открытие существующего файла на запись приводит к отбрасыванию его старого содержимого. Попытка чтения несуществующего файла является ошибкой. Ошибки могут быть обусловлены и другими причинами (например, попыткой чтения из файла, не имея на то разрешения). При наличии какой-либо ошибки функция возвращает нулевое значение указателя NULL (которое для удобства также определяется в файле stdio.h).

Другой необходимой вещью является способ чтения или записи, если файл уже открыт. Здесь имеется несколько возможностей, из которых getc и putc являются простейшими. Функция getc возвращает следующий символ из файла; ей необходим указатель файла, чтобы знать, из какого файла читать. Таким образом,

c=getc(fp)

помещает в переменную c следующий символ из файла, указанного посредством fp, или EOF, если достигнут конец файла.

Функция putc, являющаяся обращением к функции getc,

putc(c,fp)

помещает символ из переменной c в файл FP и возвращает симмол, подобно getc. Подобно функциям getchar и putchar, функции getc и putc могут быть макросами, а не функциями.

При запуске программы автоматически открываются три файла, которые снабжены определенными указателями файлов. Этими файлами являются стандартный ввод, стандартный вывод и стандартный вывод ошибок; соответствующие указатели файлов называются stdin, stdout и stderr. Обычно все эти указатели связаны с терминалом, но stdin и stdout могут быть перенаправлены на файлы или в канальный поток pipe, как описывалось в разделе 8.2.

Функции getchar и putchar могут быть определены в терминах getc, putc, stdin и stdout следующим образом:

#define getchar() getc(stdin)

#define putchar(c) putc(c,stdout)

При работе с файлами для форматного ввода и вывода можно использовать функции fscanf и fprintf. Они идентичны функциям scanf и printf, за исключением того, что первым аргументом является указатель файла, определяющий тот файл, который будет читаться или куда будет вестись запись; управляющая строка будет вторым аргументом.

Пример 9-4. Покончив с предварительными замечаниями, мы теперь в состоянии написать программу cat для конкатенации файлов. Используемая здесь основная схема оказывается удобной во многих программах: если имеются аргументы в командной строке, то они обрабатываются последовательно. Если такие аргументы отсутствуют, то обрабатывается стандартный ввод. Это позволяет использовать программу как самостоятельно, так и как часть большей задачи.

// cat: Объединить (от англ. concatenate) файлы

#include <stdio.h>

main(int argc, char *argv[])

{

FILE *fp, *fopen();

if(argc==1) /*no args; copy standard input*/

filecopy(stdin);

else

while (--argc > 0)

if ((fp=fopen(*++argv,"r"))== NULL)

{

printf("cat: не открыть %&bsol;n",*argv);

break;

}

else

{

filecopy(fp);

fclose(fp);

}

}

// filecopy: Копирование файла в стандартный вывод

filecopy(FILE *fp)

{

int c;

while ((c=getc(fp)) != EOF)

putc(c, stdout);

}

Указатели файлов stdin и stdout заранее определены в библиотеке ввода-вывода как стандартный ввод и стандартный вывод; они могут быть использованы в любом месте, где можно использовать объект типа FILE *. Они однако являются константами, а не переменными, так что не пытайтесь им что-либо присваивать.

Функция fclose является обратной по отношению к fopen; она разрывает связь между указателем файла и внешним именем, установленную функцией fopen, и высвобождает указатель файла для другого файла. Большинство операционных систем имеют некоторые ограничения на число одновременно открытых файлов, которыми может распоряжаться программа. Поэтому, то, как мы поступили в cat, освободив не нужные нам более объекты, является хорошей идеей. Имеется и другая причина для применения функции fclose к выходному файлу - она вызывает выдачу информации из буфера, в котором putc собирает вывод. (При нормальном завершении работы программы функция fclose вызывается автоматически для каждого открытого файла).