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, за исключением того, что первым аргументом является указатель файла, определяющий тот файл, который будет читаться или куда будет вестись запись; управляющая строка будет вторым аргументом.
Покончив с предварительными замечаниями, мы теперь в состоянии написать программу 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 в качетсве возвращаемого значения свидетельствует о том, что все в порядке, а различные ненулевые значения являются признаками нормальных ситуаций.
Функция 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);
\)
Упражнение 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.
7.9.3. Обращение к системе Функция SYSTEM(S) выполняет команду, содержащуюся в символьной строке S, и затем возобновляет выполнение текущей программы. Содержимое S сильно зависит от используемой операционной системы. В качестве тривиального примера, укажем, что на системе UNIX строка