Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган
- Дата:01.10.2024
- Категория: Компьютеры и Интернет / Программирование
- Название: Язык программирования Си. Издание 3-е, исправленное
- Автор: Брайан Керниган
- Просмотров:0
- Комментариев:0
Шрифт:
Интервал:
Закладка:
printf("вepно: %sn", line); /* в виде mm/dd/yy */
else
printf("неверно: %sn", line); /* неверная форма даты */
}
Обращения к scanf могут перемежаться с вызовами других функций ввода. Любая функция ввода, вызванная после scanf, продолжит чтение с первого еще непрочитанного символа.
В завершение еще раз напомним, что аргументы функций scanf и sscanf должны быть указателями.
Одна из самых распространенных ошибок состоит в том, что вместо того, чтобы написать
scanf ("%d",&n);
пишут
scanf("%d", n);
Компилятор о подобной ошибке ничего не сообщает.
Упражнение 7.4. Напишите свою версию scanf по аналогии с minprintf из предыдущего параграфа.
Упражнение 7.5. Перепишите основанную на постфиксной записи программу калькулятора из главы 4 таким образом, чтобы для ввода и преобразования чисел она использовала scanf и/или sscanf.
7.5 Доступ к файлам
Во всех предыдущих примерах мы имели дело со стандартным вводом и стандартным выводом, которые для программы автоматически предопределены операционной системой конкретной машины.
Следующий шаг - научиться писать программы, которые имели бы доступ к файлам, заранее не подсоединенным к программам. Одна из программ, в которой возникает такая необходимость, - это программа cat, объединяющая несколько именованных файлов и направляющая результат в стандартный вывод. Функция cat часто применяется для выдачи файлов на экран, а также как универсальный "коллектор" файловой информации для тех программ, которые не имеют возможности обратиться к файлу по имени. Например, команда
cat x.c y.c
направит в стандартный вывод содержимое файлов x.c и y.c (и ничего более).
Возникает вопрос: что надо сделать, чтобы именованные файлы можно было читать; иначе говоря, как связать внешние имена, придуманные пользователем, с инструкциями чтения данных?
На этот счет имеются простые правила. Для того чтобы можно было читать из файла или писать в файл, он должен быть предварительно открыт с помощью библиотечной функции fopen. Функция fopen получает внешнее имя типа x.c или y.c, после чего осуществляет некоторые организационные действия и "переговоры" с операционной системой (технические детали которых здесь не рассматриваются) и возвращает указатель, используемый в дальнейшем для доступа к файлу.
Этот указатель, называемый указателем файла, ссылается на структуру, содержащую информацию о файле (адрес буфера, положение текущего символа в буфере, открыт файл на чтение или на запись, были ли ошибки при работе с файлом и не встретился ли конец файла). Пользователю не нужно знать подробности, поскольку определения, полученные из ‹stdio.h›, включают описание такой структуры, называемой FILE.
Единственное, что требуется для определения указателя файла, - это задать описания такого, например, вида:
FILE *fp;
FILE *fopen(char *name, char *mode);
Это говорит, что fp есть указатель на FILE, a fopen возвращает указатель на FILE. Заметим, что FILE - это имя типа) наподобие int, а не тег структуры. Оно определено с помощью typedef. (Детали того, как можно реализовать fopen в системе UNIX, приводятся в параграфе 8.5.)
Обращение к fopen в программе может выглядеть следующим образом:
fp = fopen(name, mode);
Первый аргумент - строка, содержащая имя файла. Второй аргумент несет информацию о режиме. Это тоже строка: в ней указывается, каким образом пользователь намерен применять файл. Возможны следующие режимы: чтение (read - "r"), запись (write - "w") и добавление (append - "a"), т. е. запись информации в конец уже существующего файла. В некоторых системах различаются текстовые и бинарные файлы; в случае последних в строку режима необходимо добавить букву "b" (binary - бинарный).
Тот факт, что некий файл, которого раньше не было, открывается на запись или добавление, означает, что он создается (если такая процедура физически возможна). Открытие уже существующего файла на запись приводит к выбрасыванию его старого содержимого, в то время как при открытии файла на добавление его старое содержимое сохраняется. Попытка читать несуществующий файл является ошибкой. Могут иметь место и другие ошибки; например, ошибкой считается попытка чтения файла, который по статусу запрещено читать. При наличии любой ошибки fopen возвращает NULL. (Возможна более точная идентификация ошибки; детальная информация по этому поводу приводится в конце параграфа 1 приложения B.)
Следующее, что нам необходимо знать, - это как читать из файла или писать в файл, коль скоро он открыт. Существует несколько способов сделать это, из которых самый простой состоит в том, чтобы воспользоваться функциями getc и putc. Функция getc возвращает следующий символ из файла; ей необходимо сообщить указатель файла, чтобы она знала откуда брать символ.
int getc(FILE *fp);
Функция getc возвращает следующий символ из потока, на который указывает *fp; в случае исчерпания файла или ошибки она возвращает EOF.
Функция putc пишет символ c в файл fp
int putc(int с, FILE *fp);
и возвращает записанный символ или EOF в случае ошибки. Аналогично getchar и putchar, реализация getc и putc может быть выполнена в виде макросов, а не функций.
При запуске Си-программы операционная система всегда открывает три файла и обеспечивает три файловые ссылки на них. Этими файлами являются: стандартный ввод, стандартный вывод и стандартный файл ошибок; соответствующие им указатели называются stdin, stdout и stderr; они описаны в ‹stdio.h›. Обычно stdin соотнесен с клавиатурой, а stdout и stderr - с экраном. Однако stdin и stdout можно связать с файлами или, используя конвейерный механизм, соединить напрямую с другими программами, как это описывалось в параграфе 7.1.
С помощью getc, putc, stdin и stdout функции getchar и putchar теперь можно определить следующим образом:
#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
Форматный ввод-вывод файлов можно построить на функциях fscanf и fprintf. Они идентичны scanf и printf с той лишь разницей, что первым их аргументом является указатель на файл, для которого осуществляется ввод-вывод, формат же указывается вторым аргументом.
int fscanf(FILE *fp, char *format,…)
int fprintf(FILE *fp, char *format,…)
Вот теперь мы располагаем теми сведениями, которые достаточны для написания программы cat, предназначенной для конкатенации (последовательного соединения) файлов. Предлагаемая версия функции cat, как оказалось, удобна для многих программ. Если в командной строке присутствуют аргументы, они рассматриваются как имена последовательно обрабатываемых файлов. Если аргументов нет, то обработке подвергается стандартный ввод.
#include ‹stdio.h›
/* cat: конкатенация файлов, версия 1 */
main(int argc, char *argv[])
{
FILE *fp;
void filecopy(FILE *, FILE *);
if (argc == 1) /* нет аргументов; копируется стандартный ввод */
filecopy(stdin, stdout);
else
while (--argc › 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
printf("cat: не могу открыть файл %sn", *argv);
return 1;
} else {
filecopy(fp, stdout);
fclose(fp);
}
return 0;
}
/* filecopy: копирует файл ifp в файл ofp */
void filecopy(FILE *ifp, FILE *ofp)
{
int c;
while ((c = getc(ifp)) != EOF)
putc(c, ofp);
}
Файловые указатели stdin и stdout представляют собой объекты типа FILE*. Это константы, а не переменные, следовательно, им нельзя ничего присваивать.
Функция
int fclose(FILE *fp)
- обратная по отношению к fopen; она разрывает связь между файловым указателем и внешним именем (которая раньше была установлена с помощью fopen), освобождая тем самым этот указатель для других файлов. Так как в большинстве операционных систем количество одновременно открытых одной программой файлов ограничено, то файловые указатели, если они больше не нужны, лучше освобождать, как это и делается в программе cat. Есть еще одна причина применить fclose к файлу вывода, - это необходимость "опорожнить" буфер, в котором putc накопила предназначенные для вывода данные. При нормальном завершении работы программы для каждого открытого файла fclose вызывается автоматически. (Вы можете закрыть stdin и stdout, если они вам не нужны. Воспользовавшись библиотечной функцией freopen, их можно восстановить.)
- Язык программирования C++. Пятое издание - Стенли Липпман - Программирование
- Вопросы истории: UNIX, Linux, BSD и другие - Федорчук Алексей Викторович "alv" - Прочая околокомпьтерная литература
- Принципы назначения наказания - Самир Велиев - Юриспруденция
- Литература и революция. Печатается по изд. 1923 г. - Лев Троцкий - Публицистика
- Неизвестная революция 1917-1921 - Всеволод Волин - История