11.5
Библиотеки
Предположим, что в файле необходимо хранить однотипные объекты
переменной длины. Например, некоторое множество матриц или изображений. В этом
случае поступают следующим образом: каждому информационному объекту
присваивается некоторый идентификатор (это может быть имя или некоторый номер),
файл разбивается на две части: заголовочная часть и часть хранения объектов.
Заголовочная часть хранит таблицу, в которой записано: идентификатор объекта,
размер объекта и указатель на расположение этого объекта в файле. Такая
организация называется библиотечной.
- Основные библиотечные операции:
- создать библиотечный файл;
- записать объект в библиотеку с заданным
идентификатором;
- прочитать объект по указанному идентификатору;
- удалить объект из библиотеки. Рассмотрим более
подробнее создание и использование библиотечной организации файла. Прежде
всего, запишем описание заголовка библиотеки.
- Структура загалока представлена
ниже:
-
// описание заголовка
библиотеки
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 выполняет
следующие действия:
- Переписывается заголовок
библиотеки из блока управления.
- Переписывается таблица имен
элементов.
- Закрывается файл библиотеки.
Структура 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();
}
|