Смекни!
smekni.com

должен быть указателем на символы; следующий вводимый

символ помещается в указанное место. Обычный пропуск сим-

волов пустых промежутков в этом случае подавляется; для

чтения следующего символа, который не является символом

пустого промежутка, пользуйтесь спецификацией преобразо-

вания %1S.

S - Ожидается символьная строка; соответствующий аргумент

должен быть указателем символов, который указывает на

массив символов, который достаточно велик для принятия

строки и добавляемого в конце символа \0.

F - Ожидается число с плавающей точкой; соответствующий ар-

гумент должен быть указателем на переменную типа FLOAT.

Е - символ преобразования E является синонимом для F. Формат

ввода переменной типа FLOAT включает необязательный знак,

строку цифр, возможно содержащую десятичную точку и нео-

бязательное поле экспоненты, состоящее из буквы E, за ко-

торой следует целое, возможно имеющее знак.

· 158 -

Перед символами преобразования 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 и поместит

в NAME строку “45”. при следующем обращении к любой процеду-

ре ввода рассмотрение начнется с буквы A. В этих двух приме-

рах NAME является указателем и, следовательно, перед ним не

нужно помещать знак &.

В качестве другого примера перепишем теперь элементарный

калькулятор из главы 4, используя для преобразования ввода

функцию SCANF:

#INCLUDE <STDIO.H>

MAIN() /* RUDIMENTARY DESK CALCULATOR */

&bsol;(

DOUBLE SUM, V;

SUM =0;

WHILE (SCANF(“%LF”, &V) !=EOF)

PRINTF(“&bsol;T%.2F&bsol;N”, SUM += V);

&bsol;)

выполнение функции SCANF заканчивается либо тогда, когда она

исчерпывает свою управляющую строку, либо когда некоторый

элемент ввода не совпадает с управляющей спецификацией. В

качестве своего значения она возвращает число правильно сов-

падающих и присвоенных элементов ввода. Это число может быть

· 159 -

использовано для определения количества найденных элементов

ввода. при выходе на конец файла возвращается EOF; подчерк-

нем, что это значение отлично от 0, что следующий вводимый

символ не удовлетворяет первой спецификации в управляющей

строке. При следующем обращении к SCANF поиск возобновляется

непосредственно за последним введенным символом.

Заключительное предостережение: аргументы функции SCANF

должны быть указателями. Несомненно наиболее распространен-

ная ошибка состоит в написании

SCANF(“%D”, N);

вместо

SCANF(“%D”, &N);

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

От функции SCANF и PRINTF происходят функции SSCANF и

SPRINTF, которые осуществляют аналогичные преобразования, но

оперируют со строкой, а не с файлом. Обращения к этим функ-

циям имеют вид:

SPRINTF(STRING, CONTROL, ARG1, ARG2, ...)

SSCANF(STRING, CONTROL, ARG1, ARG2, ...)

Как и раньше , функция SPRINTF преобразует свои аргументы

ARG1, ARG2 и т.д. В соответствии с форматом, указанным в

CONTROL, но помещает результаты в STRING, а не в стандартный

вывод. KОнечно, строка 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.

Упражнение 7-2.

Перепишите настольный калькулятор из главы 4, используя

для ввода и преобразования чисел SCANF и/или SSCANF.

·
160 -

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

Все до сих пор написанные программы читали из стандарт-

ного ввода и писали в стандартный вывод, относительно кото-

рых мы предполагали, что они магическим образом предоставле-

ны программе местной операционной системой.

Следующим шагом в вопросе ввода-вывода является написа-

ние программы, работающей с файлом, который не связан зара-

нее с программой. одной из программ, которая явно демонстри-

рует потребность в таких операциях, является CAT, которая

объединяет набор из нескольких именованных файлов в стандар-

тный вывод. Программа CAT используется для вывода файлов на

терминал и в качестве универсального сборщика ввода для

программ, которые не имеют возможности обращаться к файлам

по имени. Например, команда

CAT X.C.Y.C

печатает содержимое файлов X.C и Y.C в стандартный вывод.

Вопрос состоит в том, как организовать чтение из имено-

ванных файлов, т.е., как связать внешние имена, которыми

мыслит пользователь, с фактически читающими данные операто-

рами.

Эти правила просты. Прежде чем можно считывать из неко-

торого файла или записывать в него, этот файл должен быть

открыт с помощью функции FOPEN из стандартной библиотеки.

функция FOPEN берет внешнее имя (подобное X.C или Y.C), про-

водит некоторые обслуживающие действия и переговоры с опера-

ционной системой (детали которых не должны нас касаться) и

возвращает внутреннее имя, которое должно использоваться при

последующих чтениях из файла или записях в него.

Это внутреннее имя, называемое “указателем файла”, фак-

тически является указателем структуры, которая содержит ин-

формацию о файле, такую как место размещения буфера, текущая

позиция символа в буфере, происходит ли чтение из файла или

запись в него и тому подобное. Пользователи не обязаны знать

эти детали, потому что среди определений для стандартного

ввода-вывода, получаемых из файла STDIO.H, содержится опре-

деление структуры с именем FILE. Единственное необходимое

для указателя файла описание демонстрируется примером:

FILE *FOPEN(), *FP;

Здесь говорится, что FP является указателем на FILE и

FOPEN возвращает указатель на FILE. Oбратите внимание, что

FILE является именем типа, подобным INT, а не ярлыку струк-

туры; это реализовано как TYPEDEF. (Подробности того, как

все это работает на системе UNIX, приведены в главе 8).

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

вид:

FP=FOPEN(NAME,MODE);

·
161 -

Первым аргументом функции FOPEN является “имя” файла, кото-

рое задается в виде символьной строки. Второй аргумент MODE

(“режим”) также является символьной строкой, которая указы-

вает, как этот файл будет использоваться. Допустимыми режи-

мами являются: чтение (“R”), запись (“W”) и добавление

(“A”).

Если вы откроете файл, который еще не сущетвует, для за-

писи или добавления, то такой файл будет создан (если это

возможно). Открытие существующего файла на запись приводит к

отбрасыванию его старого содержимого. Попытка чтения несу-

ществующего файла является ощибкой. Ошибки могут быть обус-

ловлены и другими причинами (например, попыткой чтения из

файла, не имея на то разрешения). При наличии какой-либо

ошибки функция возвращает нулевое значение указателя NULL

(которое для удобства также определяется в файле STDIO.H).

Другой необходимой вещью является способ чтения или за-

писи, если файл уже открыт. Здесь имеется несколько возмож-

ностей, из которых GETC и PUTC являются простейшими.функция

GETC возвращает следующий символ из файла; ей необходим ука-

затель файла, чтобы знать, из какого файла читать. Таким об-

разом,

C=GETC(FP)

помещает в “C” следующий символ из файла, указанного посред-

ством FP, и EOF, если достигнут конец файла.

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

PUTC(C,FP)

помещает символ “C” в файл FP и возвращает “C”. Подобно фун-кциям GETCHAR и PUTCHAR, GETC и PUTC могут быть макросами, а не функциями.

При запуске программы автоматически открываются три фай-

ла, которые снабжены определенными указателями файлов. Этими

файлами являются стандартный ввод, стандартный вывод и стан-

дартный вывод ошибок; соответствующие указатели файлов назы-

ваются STDIN, STDOUT и STDERR. Обычно все эти указатели свя-

заны с терминалом, но STDIN и STDOUT могут быть перенаправ-

лены на файлы или в поток (PIPE), как описывалось в разделе

7.2.

Функции GETCHAR и PUTCHAR могут быть определены в терми-

налах GETC, PUTC, STDIN и STDOUT следующим образом:

#DEFINE GETCHAR() GETC(STDIN) #DEFINE PUTCHAR© PUTC(C,

STDOUT)

При работе с файлами для форматного ввода и вывода можно ис-

пользовать функции FSCANF и FPRINTF. Они идентичны функциям

SCANF и PRINTF, за исключением того, что первым аргументом

является указатель файла, определяющий тот файл, который бу-

дет читаться или куда будет вестись запись; управляющая

строка будет вторым аргументом.

· 162 -

Покончив с предварительными замечаниями, мы теперь в

состоянии написать программу CAT для конкатенации файлов.

Используемая здесь основная схема оказывается удобной во

многих программах: если имеются аргументы в командной стро-

ке, то они обрабатываются последовательно. Если такие аргу-

менты отсутствуют, то обрабатывается стандартный ввод. Это

позволяет использовать программу как самостоятельно, так и

как часть большей задачи.

#INCLUDE <STDIO.H>

MAIN(ARGC, ARGV) /*CAT: CONCATENATE FILES*/

INT ARGC;

CHAR *ARGV[];

&bsol;(

FILE *FP, *FOPEN();