Реферат: Язык С
или 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 */
\(
DOUBLE SUM, V;
SUM =0;
WHILE (SCANF(“%LF”, &V) !=EOF)
PRINTF(“\T%.2F\N”, SUM += V);
\)
выполнение функции 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[];
\(
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:CAN'T OPEN %\N”,*ARGV);
BREAK;
\) ELSE \(
FILECOPY(FP);
FCLOSE(FP);
\)
\)
FILECOPY(FP) /*COPY FILE FP
TO STANDARD OUTPUT*/
FILE *FP;
\(
INT C;
WHILE ((C=GETC(FP)) !=EOF)
PUTC(C, STDOUT);
\)
Указатели файлов STDIN и STDOUT
заранее определены в библио-
теке ввода-вывода как стандартный
ввод и стандартный вывод;
они могут быть использованы в любом
месте, где можно исполь-
зовать объект типа FILE*.они однако
являются константами, а
не переменными, так что не
пытайтесь им что-либо присваи-
вать.
Функция FCLOSE является
обратной по отношению к FOPEN;
она разрывает связь между
указателем файла и внешним именем,
установленную функцией FOPEN, и
высвобождает указатель файла
для другого файла.большинство
операционных систем имеют не-
которые ограничения на число
одновременно открытых файлов,
которыми может распоряжаться
программа. Поэтому, то как мы
поступили в CAT, освободив не
нужные нам более объекты, яв-
ляется хорошей идеей. Имеется и
другая причина для примене-
ния функции FCLOSE к выходному
файлу - она вызывает выдачу
информации из буфера, в котором
PUTC собирает вывод. (При
нормальном завершении работы
программы функция FCLOSE вызы-
вается автоматически для
каждого открытого файла).
·
163 -
7.7. Обработка ошибок
- STDERR и EXIT
Обработка ошибок в CAT
неидеальна. Неудобство заключает-
ся в том, что если один из файлов
по некоторой причине ока-
зывается недоступным,
диагностическое сообщение об этом пе-
чатается в конце объединенного
вывода. Это приемлемо, если
вывод поступает на терминал, но не
годится, если вывод пос-
тупает в некоторый файл или через
поточный (PIPELINE) меха-
низм в другую программу.
Чтобы лучше обрабатывать такую
ситуацию, к программе
точно таким же образом, как STDIN и
STDOUT, присоединяется
второй выходной файл, называемый
STDERR. Если это вообще
возможно, вывод, записанный в файле
STDERR, появляется на
терминале пользователя, даже если
стандартный вывод направ-
ляется в другое место.
Давайте переделаем программу
CAT таким образом, чтобы
сообщения об ошибках писались в
стандартный файл ошибок.
“INCLUDE <STDIO.H>
MAIN(ARGC,ARGV) /*CAT: CONCATENATE
FILES*/
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(STDERR,
“CAT: CAN'T OPEN,%S\N”, ARGV);
EXIT(1);
\) ELSE \(
FILECOPY(FP);
\)
EXIT(0);
\)
Программа сообщает об ошибках двумя
способами. Диагностичес-
кое сообщение, выдаваемое функцией
FPRINTF, поступает в
STDERR и, таким образом,
оказывается на терминале пользова-
теля, а не исчезает в потоке
(PIPELINE) или в выходном фай-
ле.
Программа также использует
функцию EXIT из стандартной
библиотеки, обращение к которой
вызывает завершение выполне-
ния программы. Аргумент функции
EXIT доступен любой програм-
ме, обращающейся к данной функции,
так что успешное или неу-
дачное завершение данной программы
может быть проверено дру-
гой программой, использующей эту в
качестве подзадачи. По
соглашению величина 0 в качетсве
возвращаемого значения сви-
детельствует о том, что все в
порядке, а различные ненулевые
значения являются признаками
нормальных ситуаций.
·
164 -
Функция EXIT вызывает функцию
FCLOSE для каждого откры-
того выходного файла, с тем чтобы
вывести всю помещенную в
буферы выходную информацию, а затем
вызывает функцию _EXIT.
Функция _EXIT приводит к
немедленному завершению без очистки
каких-либо буферов; конечно, при
желании к этой функции мож-
но обратиться непосредственно.
7.8. Ввод и вывод строк
Стандартная библиотека
содержит функцию FGETS, совершен-
но аналогичную функции GETLINE,
которую мы использовали на
всем протяжении книги. В результате
обращения
FGETS(LINE, MAXLINE, FP)
следующая строка ввода (включая
символ новой строки) считы-
вается из файла FP в символьный
массив LINE; самое большое
MAXLINE_1 символ будет прочитан.
Результирующая строка за-
канчивается символом \ 0. Нормально
функция FGETS возвращает
LINE; в конце файла она возвращает
NULL. (Наша функция
GETLINE возвращает длину строки, а
при выходе на конец файла
·
нуль).
Предназначенная для вывода
функция FPUTS записывает
строку (которая не обязана
содержать символ новой строки) в
файл:
FPUTS(LINE, FP)
Чтобы показать, что в функциях
типа FGETS и FPUTS нет ничего таинственного, мы приводим их ниже,
скопированными непосредственно из стандартной библиотеки ввода-вывода:
#INCLUDE <STDIO.H>
CHAR *FGETS(S,N,IOP) /*GET AT MOST
N CHARS FROM IOP*/
CHAR *S;
INT N;
REGISTER FILE *IOP;
\(
REGISTER INT C;
REGISTER CHAR *CS;
CS = S;
WHILE(--N>0&&(C=GETC(IOP))
!=EOF)
IF ((*CS++ = C)=='\N')
BREAK;
*CS = '\0';
RETURN((C==EOF && CS==S) 7 NULL
: S);
\)
FPUTS(S,IOP) /*PUT STRING S ON
FILS IOP*/
REGISTER CHAR *S;
REGISTER FILE *IOP;
\(
REGISTER INT C;
WHILE (C = *S++)
PUTC(C,IOP);
\)
·
165 -
Упражнение 7-3.
Напишите программу сравнения
двух файлов, которая будет
печатать первую строку и
позицию символа, где они различают-
ся.
Упражнение 7-4.
Переделайте программу поиска
заданной комбинации симво-
лов из главы 5 таким образом, чтобы
в качестве ввода исполь-
зовался набор именованных файлов
или, если никакие файлы не
указаны как аргументы, стандартный
ввод. Следует ли печатать
имя файла при нахождении подходящей
строки?
Упражнение 7-5.
Напишите программу печати
набора файлов, которая начина-
ет каждый новый файл с новой
страницы и печатает для каждого
файла заголовок и счетчик
текущих страниц.
7.9. Несколько разнообразных
функций
Стандартная библиотека
предоставляет множество разнооб-
разных функций, некоторые из
которых оказываются особенно
полезными. Мы уже упоминали функции
для работы со строками:
STRLEN, STRCPY, STRCAT и STRCMP.
Вот некоторые другие.
7.9.1. Проверка
вида символов и преобразования
Некоторые макросы выполняют
проверку символов и преобра-
зования:
SALPHA© не 0, если “C” алфавитный
символ,
0 - если нет.
SUPPER© Не 0, если “C” буква
верхнего регистра,
0 - если нет.
SLOWER© Не 0, если “C” буква
нижнего регистра,
0 - если нет.
SDIGIT© Не 0, если “C” цифра,
0 - если нет.
SSPACL© Не 0, если “C” пробел,
табуляция
или новая строка, 0 - если нет.
OUPPER© Преобразует “C” в букву
верхнего регистра.
OLOWER© Преобразует “C” в букву
нижнего регистра.
7.9.2. Функция
UNGETC
Стандартная библиотека
содержит довольно ограниченную
версию функции UNGETCH, написанной
нами в главе 4; она назы-
вается UNGETC. В результате
обращения
UNGETC(C,FP)
символ “C” возвращается в файл FP.
Позволяется возвращать в
каждый файл только один символ.
Функция UNGETC может быть
использована в любой из функций
ввода и с макросами типа
SCANF, GETC или GETCHAR.
·
166 -
7.9.3. Обращение к системе
Функция SYSTEM(S)
выполняет команду, содержащуюся в сим-
вольной строке S, и затем
возобновляет выполнение текущей
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
|