Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2557 / 712 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 5:

Файловый ввод/вывод

Функция fopen() из группы буферизованного ввода/вывода по сути аналогична open(), только вместо файлового дескриптора в качестве результата возвращается указатель на объект, служащий для управления сформированным потоком (в случае неудачи результат равен пустому указателю NULL ).

Второй аргумент, mode, определяющий допустимые виды операций ввода/вывода, задается как цепочка символов. Он может принимать следующие значения.

"r"

Открыть файл на чтение.

"w"

Опустошить или создать файл, открыв его на запись.

"a"

Открыть или создать файл на запись в конец.

"r+"

Открыть файл на изменение (чтение и запись).

"w+"

Опустошить или создать файл, открыв его на изменение.

"a+"

Открыть или создать файл на изменение с записью в конец.

(Стандарт языка C позволяет приписывать к перечисленным цепочкам символ 'b', который, впрочем, ни на что не влияет.)

Если открытый файл не соответствует интерактивному устройству, ассоциированный с ним поток полностью буферизуется.

Функция fdopen() формирует поток, ассоциированный с дескриптором ранее открытого файла.

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

Функция freopen() предназначена для ассоциирования существующего потока (третий аргумент, stream ) с заданным файлом (первый аргумент, path ) и разрешенными видами операций ввода/вывода (второй аргумент, mode ).

В первую очередь freopen() пытается вытолкнуть буфера потока stream и закрыть ассоциированный с ним файл. Неудача данного действия ни на что не влияет.

Затем, аналогично fopen(), открывается заданный файл, только без формирования нового потока ; результатом служит stream (лишенный, правда, ориентации) или NULL (в случае неудачи).

Если в качестве первого аргумента функции freopen() задан пустой указатель, делается попытка изменить виды операций, разрешенные для потока   stream. (Формально можно считать, что первым аргументом freopen() служит имя файла, ассоциированного с этим потоком.) От реализации зависит, разрешены ли подобные действия вообще и при каких условиях они завершаются успешно.

Для закрытия файлов (точнее, файловых дескрипторов или потоков ) предназначены функции close() и fclose() (см. листинг 5.3).

#include <unistd.h>
int close (int fildes);
#include <stdio.h>
int fclose (FILE *stream);
Листинг 5.3. Описание функций close() и fclose().

Функция close() освобождает файловый дескриптор fildes, который становится доступным для последующего использования при открытии файлов.

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

Если число жестких ссылок на файл равно нулю и закрывается последний ассоциированный с ним дескриптор, файл перестает быть доступным, а занимавшееся им пространство освобождается.

Когда закрывается последний дескриптор, ассоциированный с каналом, все оставшиеся непрочитанными данные теряются.

Функция close() возвращает 0 в случае успешного завершения и -1 при неудаче.

Функция fclose() по сути аналогична, только она освобождает поток, выталкивая при этом буфера. Признаком успешного завершения также служит 0, признаком неудачи – константа EOF.

Приведем примеры использования описанных функций. Сочетание флагов O_CREAT и O_EXCL функции open() позволяет организовать проверку и создание файлов-замков, для которых важен факт существования в одном экземпляре, а не содержимое (см. листинг 5.4).

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#define LOCK_FILE "my_lock"
/* Функция пытается создать файл-замок */
/* Результат равен 0 в случае успеха, */
/* 1, если файл уже существует, */
/* -1 в случае прочих ошибок */
static int gate (const char *lock_name) {
	int fd;
	if ((fd = open (lock_name, O_WRONLY | O_CREAT |
		O_EXCL, (mode_t) 0)) < 0) {
		if (errno == EEXIST) {
			return (1);
		}
		return (-1);
	}
	return (close (fd));
}
int main (void) {
	int res;
	if ((res = gate (LOCK_FILE)) > 0) {
		perror ("Ошибка при создании файла-замка "
		LOCK_FILE);
	} else if (res == 1) {
		fprintf (stderr, "Файл-замок " LOCK_FILE "
			уже существует\n");
	}
	return (res);
}
Листинг 5.4. Пример программы, использующей функции open() и close().

Читателю предлагается выполнить приведенную программу дважды.

Следующая программа иллюстрирует перенаправление стандартного вывода в файл (см. листинг 5.5). Ее тоже полезно выполнить дважды и затем самостоятельно осмыслить результаты.

#include <stdio.h>
#define LOGFILE "my_logfile"
int main (void) {
	FILE *fp;
	printf ("До перенаправления стандартного вывода
		в файл " LOGFILE "\n");
	if ((fp = freopen (LOGFILE, "a", stdout)) ==
		NULL) {
		perror ("Не удалось перенаправить стандартный
			вывод в файл " LOGFILE);
		return (-1);
	}
	printf ("После перенаправления стандартного
		вывода в файл " LOGFILE "\n");
	if (fclose (fp) == EOF) {
		perror ("Не удалось закрыть файл " LOGFILE);
		return (-1);
	}
	printf ("После закрытия файла " LOGFILE "\n");
	return (0);
}
Листинг 5.5. Перенаправление стандартного вывода с помощью функции freopen().

Весьма полезной с практической точки зрения является функция создания и открытия временных файлов tmpfile() (см. листинг 5.6).

#include <stdio.h>
FILE *tmpfile (void);
Листинг 5.6. Описание функции tmpfile().

Временный файл открывается на изменение ( w+ ) и автоматически удаляется после закрытия всех ссылок на него.

Использование функции tmpfile() предпочтительнее генерации "временного" имени с помощью функции tmpnam() и последующего создания файла с этим именем, поскольку в промежутке какой-либо другой процесс может создать одноименный файл.

Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?