.: [предыдущая | оглавление | следующая] :.

11.5 Библиотеки

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

Основные библиотечные операции:
  1. создать библиотечный файл;
  2. записать объект в библиотеку с заданным идентификатором;
  3. прочитать объект по указанному идентификатору;
  4. удалить объект из библиотеки. Рассмотрим более подробнее создание и использование библиотечной организации файла. Прежде всего, запишем описание заголовка библиотеки.
Структура загалока представлена ниже:
// описание заголовка библиотеки
typedef struct LibraryHeaderTag
{
      char type[10]; //тип файла
      char date[10]; //дата создания
      int version; //версия
      int size; //количество объектов
      int maxsize; //максимальное количество объектов
      int size_del; //количество удаленных
      int sizelib; //размер библиотеки
} LibraryHeader; //Имя нового типа

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

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

Параметр size определяет количество объектов, хранящихся на данный момент в библиотеки.

Параметр maxsize определяет максимальное количество объектов которое может храниться в библиотеке.

Параметр size_del определяет количество удаленных объектов. Это необходимо для учета неиспользуемой памяти в библиотеки.

Параметр size_lib хранит текущий размер библиотеки в байтах. Это необходимо для того, чтобы знать адрес записи нового объекта в библиотеку.

Рассмотрим теперь организацию таблицы имен объектов. Описание элемента таблицы приведено ниже:
// описание объекта хранения в таблице
typedef struct LibraryElementTag
{
      char name[50]; //имя объекта
      int size; //размер объекта
      int offset; //смещение относительно начала файла
      int type; //тип объекта
} LibraryElement;

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

Блок управления библиотекой - структура, которая необходима для работы библиотечных функций.

Описание блока управления приведено ниже.
//описание блока упраления
typedef struct LibraryControlBlock
{
      FILE *hFile; //дискриптор файла
      LibraryHeader lh; //структура заголовка
      LibraryElement *le; //таблица объектов хранения
} LBC;

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

1. void InitHeader(LibraryHeader *lh,int maxsize)
2. int CreateLibrary(char *name,int maxsize)
3. int OpenLibrary(char *namelib,LBC *lbc)
4. int FindObject(char *name,LBC *lbc);
5. int CloseLibrary(LBC *lbc)
6. int AddLibrary(LBC *lbc,char *name, void* object, int sizeob);
7. int GetSizeObject(LBC *lbc,char *name);
8. int GetObject(LBC *lbc,char *name,void *object);

Функция InitHeader производит инициализацию структуры заголовка библиотеки. Устанавливается тип библиотеки, дата создания, версия, максимальное количество объектов, первоначальное значение размера библиотеки.

Ниже приведен текст программы
void InitHeader(LibraryHeader *lh,int maxsize)
{
      strcpy(lh->type,"krulib");
      strcpy(lh->date,"7.01.2004");
      lh->version=1;
      lh->size=0;
      lh->maxsize=maxsize;
      lh->size_del=0;
      lh->sizelib=sizeof(LibraryHeader)+maxsize*sizeof(LibraryElement);
}

Функция создания библиотечного файла CreateLibrary, создается файл с заданным именем name. Далее инициализируется заголовочная структура и записывается в файл, затем организуется цикл и записывается множество пустых элементов таблицы количеством maxsize.

int CreateLibrary(char *name,int maxsize)
{
      FILE *hFile;
      LibraryHeader lh;
      LibraryElement le;
      hFile=fopen(name,"a+"); //открыть файл несуществующий
      if(hFile==NULL) return -1; //обработка ошибки
      InitHeader(&lh,maxsize); //инициализировать заголовок
      fwrite(&lh,1,sizeof(LibraryHeader),hFile); //записать заголовок
      setmem(&le,sizeof(LibraryElement),0); //обнулить структуру элемента таблицы
      for(int i=0; i<maxsize; i++) //записать заданое максимальное количество элементов
            fwrite(&le,1,sizeof(LibraryElement),hFile);
      fclose(hFile); //закрыть файл
      return 1;
}

Функция открытия или создания библиотечного файла OpenFile. Первоначально вызывается библиотечная функция access, описание которой дано в заголовочном файле io.h. Эта функция проверяет наличие файла в текущем каталоге и если доступ к файлу имеется, то возвращает 0, в противном случае, код ошибки.

В нашем случае возврат нуля означает присутствие файла в каталоге. Если файл не существует, то вызывается функция CreateLibrary. Если файл существует то функция открывает файл с именем указанном в namelib. Далее читается заголовок и проверяется тип файла. Если тип совпал, то проверяется версия, и далее распределяется память под таблицы, если таблица не пуста, то читается таблица. По окончанию открытия возвращается код завершения и заполненная структура блока управления библиотекой.

int OpenLibrary(char *namelib,LBC *lbc)
{
      if(access(namelib, 0) != 0)
      { //файл с таким именем не существует?
            if(CreateLibrary(namelib,100)==-1) return -1; //да создать!
      }
      lbc->hFile=fopen(namelib,"r+b"); //открыть на чтение и перезапись с добавлением
      if(lbc->hFile==NULL) return -1;//обработка ошибки
      fseek(lbc->hFile,0L,SEEK_SET); //переместить указатель файла в начало
      fread(&lbc->lh,1,sizeof(LibraryHeader),lbc->hFile);
      //прочитать заголовок
      if(strcmp(lbc->lh.type,"krulib")!=0)
      { //проверка типа файла
            printf("Error %s type\n",namelib);
            return -2;
      }
      if(lbc->lh.version!=1)
      { //проверка версии файла
      printf("Error %s version\n",namelib);
      return -3;
      }
      //распределить память под таблицу
      lbc->le=(LibraryElement *)malloc(sizeof(LibraryElement)
      *lbc->lh.maxsize);
      if(lbc->le==NULL) return -1;
      if(lbc->lh.size>0) //если таблица не пуста, прочитать таблицу
            fread(lbc->le,1,sizeof(LibraryElement)*lbc->lh.size,lbc->hFile);
      return 1;
}

Функция закрытия библиотеки CloseLibrary выполняет следующие действия:

  1. Переписывается заголовок библиотеки из блока управления.
  2. Переписывается таблица имен элементов.
  3. Закрывается файл библиотеки.

Структура lbc должна быть актуальной.

int CloseLibrary(LBC *lbc)
{
      fseek(lbc->hFile,0L,SEEK_SET);
      fwrite(&lbc->lh,1,sizeof(LibraryHeader),lbc->hFile);
      if(lbc->lh.size>0)
            fwrite(lbc->le,1,sizeof(LibraryElement)*lbc->lh.size,
      lbc->hFile);
      fclose(lbc->hFile);
      free(lbc->le);
      return 1;
}

Функция FindObject ищет имя в таблице имен, которая размещена в блоке управления библиотекой. Если такое имя находит, то возвращает индекс. В противном случае -1.

int FindObject(char *name,LBC *lbc)
{
      for(int i=0; i<lbc->lh.size; i++)
      {
            if(strcmp(name,lbc->le[i].name)==0) return i; //нашли
      }
      return -1; //не нашли
}

Функция AddLibrary производит запись объекта в библиотеку. Предварительно структура lbc должна быть заполнена функцией OpenLibrary. Кроме того, необходимо передать имя объекта, его адрес и размер. Функция проверяет наличие такого имени в библиотеки, проверяет наличие памяти для записи в таблицу имен и если все нормально, то записывает имя, размер и адрес (местоположение объекта уже в файле) в заданный элемент таблицы. Далее записывается объект в конец файла. Затем корректируется в блоке управления размер файла.

int AddLibrary(LBC *lbc,char *name, void* object, int sizeob)
{
      if(FindObject(name,lbc)==-1)
      { //поискать объект с таким именем
            if(lbc->lh.size==lbc->lh.maxsize) return -1;
            //место еще есть?
            fseek(lbc->hFile,0L,SEEK_END); //переместить указатель файла в конец
            int i=lbc->lh.size++; //увеличить счетчик числа объектов
            strcpy(lbc->le[i].name,name); //записать имя в таблицу
            lbc->le[i].size=sizeob; //записать размер объекта
            lbc->le[i].offset=lbc->lh.sizelib; //записать смещение относительно начала файла
            fwrite((char*)object,1,sizeob,lbc->hFile);//записать объект в конец файла
            lbc->lh.sizelib+=sizeob; //изменить значения размера файла
            return 1;
      }
      else return 0; //объект с таким именем существует
}

Функция GetSizeObject определяет размер объект.

int GetSizeObject(LBC *lbc,char *name)
{
      int   addr;
      if((addr=FindObject(name,lbc))==-1) return 0; //не нашли
      return lbc->le[addr].size;
}

Функция GetObject читает объект по имени.

int GetObject(LBC *lbc,char *name,void *object)
{
      int addr;
      if((addr=FindObject(name,lbc))==0) return 0; //не нашли
      //переместить указатель файла на смещение указанное в элементе таблицы (addr)
      fseek(lbc->hFile,lbc->le[addr].offset,SEEK_SET);
      //прочитать содержимое объекта
      fread((char*)object,1,lbc->le[addr].size,lbc->hFile);
      return 1;
}

Пример использования описанных функций.

void main()
{
      LBC lbc; //объявляем блок управления библиотекой
      OpenLibrary("xxx.lib",&lbc); //создаем или открываем
      char nameo[10]="name0";
      char object[100]="object0";
      //for(int i=0; i<10; i++)
      {
            // nameo[4]=’a’+i;
            // object[6]=’0’+i;
            // AddLibrary(&lbc,nameo,object,strlen(object));
      //}
      int size;
      for(int i=0; i<10; i++)
      {
            nameo[4]=’a’+i;
            object[6]=’0’+i;
            size=GetSizeObject(&lbc,nameo);
            GetObject(&lbc,nameo,object);
            object[size]=’\ 0’;
            printf("%s\n",object);
      }
      CloseLibrary(&lbc);
      getch();
}
.: [предыдущая | оглавление | следующая] :.