Чтение онлайн

ЖАНРЫ

Язык программирования Си. Издание 3-е, исправленное

Ритчи Деннис М.

Шрифт:

void error(char *,…);

/* cp: копирование f1 в f2 */

main(int argc, char *argv[])

{

 int f1, f2, n;

 char buf[BUFSIZ];

 if (argc != 3)

error("Обращение: cp откуда куда");

 if ((f1 = open(argv[1], O_RDONLY, 0)) == -1)

error("cp: не могу открыть файл %s", argv[1]);

 if ((f2 = creat(argv[2], PERMS)) == -1)

error("cp:
не могу создать файл %s, режим %03o", argv[2], PERMS);

 while ((n = read(f1, buf, BUFSIZ)) › 0)

if (write(f2, buf, n) != n)

error ("cp: ошибка при записи в файл %s", argv[2]);

 return 0;

}

Данная программа создаст файл вывода с фиксированными правами доступа, определяемыми кодом 0666. С помощью системного вызова stat, который будет описан в параграфе 8.6, мы можем определить режим использования существующего файла и задать тот же режим для копии.

Заметим, что функция error, вызываемая с различным числом аргументов, во многом похожа на printf. Реализация error иллюстрирует, как пользоваться другими программами семейства printf. Библиотечная функция vprintf аналогична printf, с той лишь оговоркой, что переменная часть списка аргументов заменена в ней одним аргументом, который инициализируется макросом va_start. Подобным же образом соотносятся функции vfprinf с fprintf и vsprintf с sprintf.

#include ‹stdio.h›

#include ‹stdarg.h›

/* error: печатает сообщение об ошибке и умирает */

void error(char *fmt, …) {

 va_list args;

 va_start(args, fmt);

 fprintf(stderr, "ошибка: ");

 vfprintf(stderr, fmt, args);

 fprintf(stderr, "\n");

 va_end(args);

 exit(1);

}

На количество одновременно открытых в программе файлов имеется ограничение (обычно их число колеблется около 20). Поэтому любая программа, которая намеревается работать с большим количеством файлов, должна быть готова повторно использовать их дескрипторы. Функция close(int fd) разрывает связь между файловым дескриптором и открытым файлом и освобождает дескриптор для его применения с другим файлом. Она аналогична библиотечной функции fclose с тем лишь различием, что никакой очистки буфера не делает. Завершение программы с помощью exit или return в главной программе закрывает все открытые файлы.

Функция unlink(char *name) удаляет имя файла из файловой системы. Она соответствует функции remove стандартной библиотеки.

Упражнение 8.1. Перепишите программу cat из главы 7, используя функции read, write, open и close. Замените ими

соответствующие функции стандартной библиотеки. Поэкспериментируйте, чтобы сравнить быстродействие двух версий.

8.4 Произвольный доступ (lseek)

Ввод-вывод обычно бывает последовательным, т. е. каждая новая операция чтения-записи имеет дело с позицией файла, следующей за той, что была в предыдущей операции (чтения-записи). При желании, однако, файл можно читать или производить запись в него в произвольном порядке. Системный вызов lseek предоставляет способ передвигаться по файлу, не читая и не записывая данные. Так, функция

long lseek(int fd, long offset, int origin);

в файле с дескриптором fd устанавливает текущую позицию, смещая ее на величину offset относительно места, задаваемого значением origin. Значения параметра origin 0, 1 или 2 означают, что на величину offset отступают соответственно от начала, от текущей позиции или от конца файла. Например, если требуется добавить что-либо в файл (когда в командном интерпретаторе shell системы UNIX ввод перенаправлен оператором ›› в файл или когда в fopen задан аргумент "a"), то прежде чем что-либо записывать, необходимо найти конец файла с помощью вызова функции

lseek(fd, 0L, 2);

Чтобы вернуться назад, в начало файла, надо выполнить

lseek(fd, 0L, 0);

Следует обратить внимание на аргумент 0L: вместо 0L можно было бы написать (long)0 или, если функция lseek должным образом объявлена, просто 0. Благодаря lseek с файлами можно работать так, как будто это большие массивы, правда, с замедленным доступом. Например, следующая функция читает любое число байтов из любого места файла. Она возвращает число прочитанных байтов или -1 в случае ошибки.

#include "syscalls.h"

/* get: читает n байт из позиции pos */

int get(int fd, long pos, char *buf, int n)

{

 if (lseek(fd, pos, 0) ›= 0) /* установка позиции */

return read(fd, buf, n);

 else

 return -1;

}

Возвращаемое функцией lseek значение имеет тип long и является новой позицией в файле или, в случае ошибки, равно -1. Функция fseek из стандартной библиотеки аналогична lseek: от последней она отличается тем, что в случае ошибки возвращает некоторое ненулевое значение, а ее первый аргумент имеет тип FILE*.

8.5 Пример. Реализация функций fopen и getc

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

Напомним, что файлы в стандартной библиотеке описываются файловыми указателями, а не дескрипторами. Указатель файла– это указатель на структуру, содержащую информацию о файле: указатель на буфер, позволяющий читать файл большими кусками: число незанятых байтов буфера; указатель на следующую позицию в буфере; дескриптор файла; флажки, описывающие режим (чтение/запись), ошибочные состояния и т. д.

Поделиться с друзьями: