Информатика и технология программирования

         

Массив указателей в файле


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

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


//------------------------------------------------------bk59-08.cpp


void save(char *p[], char *name)
{
FILE *fd;
int i,n;
long *pp;
if ((fd=fopen(name,"wb"))==NULL) return; // Создать двоичный файл


for (n=0; p[n]!=NULL; n++); // Определить размерность МУ


pp=new long[n]; // Создать динамический массив


fwrite((void*)&#38n,sizeof(int),1,fd); // файловых указателей в памяти


fwrite((void*)pp,sizeof(long),n,fd); // Записать в файл размерность


for (i=0; i&#60n; i++) // массива файловых указателей


{ // и сам массив (занять место)


pp[i]=ftell(fd); // Записать строку в файл в виде


int sz=strlen(p[i])+1; // записи переменной длины и


fwrite((void*)&#38sz,sizeof(int),1,fd); // сохранить адрес в массиве


fwrite((void*)p[i],sz,1,fd); // файловых указателей


}
fseek(fd,sizeof(int),SEEK_SET); // Обновить в файле массив


fwrite((void*)pp,sizeof(long),n,fd); // файловых указателей


fclose(fd);
}

Функция загрузки структуры данных иллюстрирует тот факт, что при переменной размерности она должна полностью создаваться в динамический памяти.
В нашем случае это происходит в два этапа. Сначала создается и загружается массив файловых указателей, для которого создается аналогичный массив указателей на строки, но уже в памяти. Затем читаются сами строки.





//------------------------------------------------------bk59-09.cpp

char **load(char *name) // Функция возвращает динамический

{ // массив указателей на строки

FILE *fd;
int i,n;
long *pp;
char **p;
if ((fd=fopen(name,"rb"))==NULL) return;
fread((void*)&#38n,sizeof(int),1,fd); // Прочитать размерность

pp=new long[n]; // Создать динамический массив

p=new char*[n+1]; // файловых указателей и указателей

fread((void*)pp,sizeof(long),n,fd); // на строки.

// Первый - прочитать из файла

for (i=0; i&#60n; i++)
{
int sz;
fseek(fd,pp[i],SEEK_SET); // Установиться по i-му файловому

fread((void*)&#38sz,sizeof(int),1,fd); // указателю и прочитать запись

p[i]=new char[sz]; // переменной длины - строку

fread((void*)p[i],sz,1,fd);
}
p[n]=NULL;
fclose(fd);
return p;
}

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





//------------------------------------------------------bk59-10.cpp

char *load(char *name, int num) // Возвращается строка =

{ // динамический массив

FILE *fd;
int i,n,sz;
long pp;
char *p;
if ((fd=fopen(name,"rb"))==NULL) return; // Режим чтения двоичного файла

fread((void*)&#38n,sizeof(int),1,fd); // Считать размерность МУ

if (num&#62=n) return NULL; // Нет записи с таким номером

fseek(fd,sizeof(int)+sizeof(long)*num,SEEK_SET); // Установить на указатель

fread((void*)&#38pp,sizeof(long),1,fd); // с номером n и прочитать его

fseek(fd,pp,SEEK_SET); // Установиться на запись

fread((void*)&#38sz,sizeof(int),1,fd); // Прочитать длину записи

p=new char[sz]; // Создать динамический массив

fread((void*)p,sz,1,fd); // Прочитать запись - строку

fclose(fd);
return p; // Возвратить указатель на строку

}


Содержание раздела