Выбрать главу
Листинг Б.6. (read-file.c) Загрузка файла в буфер

#include <fcntl.h>

#include <stdio.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <unistd.h>

/* Загрузка содержимого файла FILENAME в память.

   Размер буфера записывается в аргумент LENGTH.

   Создаваемый буфер должен удаляться в вызывающей функции.

   Если аргумент FILENAME не соответствует обычному файлу,

   возвращается NULL. */

char* read_file(const char* filename, size_t* length) {

 int fd;

 struct stat file_info;

 char* buffer;

 /* Открытие файла. */

 fd = open(filename, O_RDONLY);

 /* Получение информации о файле. */

 fstat(fd, &file_info);

 *length = file_info.st_size;

 /* Проверка того, что это обычный файл. */

 if (!S_ISREG(file_info.st_mode)) {

  /* Этот тип файла не поддерживается. */

  close(fd);

  return NULL;

 }

 /* выделение буфера достаточного размера. */

 buffer = (char*)malloc(*length);

 /* Загрузка файла в буфер. */

 read(fd, buffer, *length);

 /* Конец работы. */

 close(fd);

 return buffer;

}

Б.3. Векторные чтение и запись

Аргументами функции write() являются указатель на буфер и длина буфера. Эта функция записывает в файл непрерывный блок данных, хранящихся в памяти. Но программам часто требуется записывать группы блоков, хранящихся по разным адресам. Если использовать функцию write(), придется либо предварительно объединять блоки в памяти, что неэффективно, либо многократно вызывать функцию. Последнее тоже не всегда приемлемо. Например, при записи в сокет два вызова функции write() приведут к отправке в сеть двух пакетов, тогда как те же самые данные можно перестать в одном пакете.

Функция writev() позволяет записать в файл несколько несвязанных буферов одновременно. Это называется векторной записью. Сложность применения функции writev() заключается в создании структуры, задающей начало и конец каждого буфера. Эта структура представляет собой массив элементов типа struct iovec. Каждый элемент описывает одну область памяти. В поле iov_base указывается адрес начала области, а в поле iov_len — ее длина. Если число буферов известно заранее, можно просто объявить массив типа struct iovec. В противном случае придется выделять массив динамически.

Функции writev() передается дескриптор записываемого файла, массив структур iovec и размер массива. Функция возвращает общее число записанных байтов.

Программа, показанная в листинге Б.7, записывает свои аргументы командной строки в файл с помощью одной-единственной функции writev(). Первый аргумент — это имя файла, в котором сохраняются все последующие аргументы, каждый в отдельной строке. Число элементов в массиве структур iovec в два раза превышает число аргументов командной строки, так как после каждого аргумента записывается символ новой строки. Поскольку количество аргументов неизвестно заранее, массив создается с помощью функции malloc().

Листинг Б.7. (write-args.c) Запись списка аргументов в файл с помощью функции writev()

#include <fcntl.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <sys/uio.h>

#include <unistd.h>

int main(int argc, char* argv[]) {

 int fd;

 struct iovec* vec;

 struct iovec* vec_next;

 int i;

 /* Символ новой строки хранится в обычной переменной

    типа char. */

 char newline = '\n';

 /* Первый аргумент командной строки -- это имя выходного

    файла. */

 char* filename = argv[1];

 /* Пропускаем первые два элемента списка аргументов.

    Элемент номер 0 -- это имя самой программы,

    а элемент номер 1 -- это имя выходного файла */

 argc -= 2;

 argv += 2;

 /* Выделяем массив элементов типа iovec каждому аргументу

    командной строки соответствует два элемента массива:

    один -- для самого аргумента,

    а другой -- для символа новой строки. */

 vec =

  (struct iovec*)malloc(2 * argc * sizeof(struct iovec));