Произвольный доступ к данным с применением блочного ввода/вывода


 

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

void rewind (FILE * fp)

Функция позиционирует указатель текущей записи в начало файла, связанного с потоком fp.

int fseek(FILE * fp, long offset, int from_where)

Сдвиг положения указателя записи вправо к концу файла fp на offset байт, если offset>0, или иначе к началу файла, если offset<0. Параметр from_where принимает значения:

SEEK_SET или 0 – сдвиг относительно начала файла,

SEEK_CUR или 1 – сдвиг относительно текущего положения указателя,

SEEK_END или 2 – сдвиг относительно конца файла.

В случае успеха функция возвращает 0, иначе EOF. Установка указателя правее старого EOF не считается ошибкой и в некоторых ситуациях в случае закрытия файла, ведет к «наращиванию» размера файла. Установка левее начала файла считается ошибкой.

Функции ftell(), fgetpos(), fsetpos() – либо возвращают, как результат вызова, значение указателя текущей записи, либо устанавливают новое значение указателя. Последние 2 функции используются, когда необходимо запомнить некоторое положение указателя в переменной, выполнить ввод или вывод, и затем восстанавливая значение указателя, вернуться к исходной записи.

int fflush(FILE * fp) (см. Лекции 3 и 14)

Функция флэширует поток ввода/вывода fp. Для ввода – это очистка буфера, а для вывода – выталкивание остатка буфера в файл. Функция возвращает EOF, если обнаруживаются ошибки во время выполнения операции. Функцию необходимо применять когда поток открыт для чтения и записи в тот момент, когда изменяется направление передачи данных, чтобы синхронизировать данные в файле и накопленные данные в буфере. При выполнении rewind() или fseek() флэширование выполняется автоматически. Так, при выполнении лабораторных работ, если обнаруживается ситуация, что операция записи выполнялась, но данные не попали в файл, необходимо применить флэширование.

Доступ к записям нефиксированного размера

Доступ к записям нефиксированного размера возможен только последовательным их просмотром. Рассмотрим пример организации указанной процедуры.

Пример

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

 

Общее число записей в файле
Запись 0 Заголовок
Ключ
Данные
Запись 1 Заголовок
Ключ
Данные

 

и т.д. ………

 

 

Далее в фрагменте программы представлена процедура формирования файла записей указанного формата и функции доступа к записям по ключу или по номеру записи в файле. Прототипы используемых библиотечных функций размещаются в следующих h-файлах: stdio, string, process, errno.

……………………

#define STOP_STRING “”

#define BUFFER_SIZE 128

#define KEY_SIZE 16

struct file_record{ // описание заголовка записи

long offset; // указатель-смещение

char indicator; // Индикатор содержимого

unsigned key_length; // длина ключа

unsigned length; // длина записи

} work; // рабочая переменная для заголовка записи

……………………………..

void main (int argc, char ** argv)

{

FILE * fptr;

int in_lines; // число введенных строк

int file_lines; // количество записей в файле

char key[KEY_SIZE]; // буфер для ввода ключа

fpos_t f_ptr; // указатель текущей записи в файле

………………………………………………………

// Проверка ввода аргументов в командной строке, в argv[1] д.б. указатель на имя файла

………………………………………………………

if((fptr = fopen(argv[1], “a+b”)) == NULL)

{

perror(“Ошибка открытия файла!”);

exit(2);

}

 

// Определение числа записей в файле

fseek(fptr, 0L, SEEK_END);

if(!ftell(fptr))

{

file_lines = 0; // файл пуст

if(!fwrite(&file_lines, 2, 1, fptr))

exit(3);

}

else

{

fseek(fptr, 0L, SEEK_SET); // файл не пуст

if(!fread(&file_lines, 2, 1, fptr))

exit(3);

fseek(fptr, 0L, SEEK_END); // позиционирование на EOF

}

………………………………………………………….

// Приглашение для ввода строк

………………………………………………………….

fflush(stdin);

// цикл ввода с клавиатуры, пустая строка – завершение цикла

for(in_lines = 0; strcmp(gets(buffer), STOP_STRING); in_lines++)

{

strcpy(key, “”); // очистка буфера ключа

printf(“Введите ключ записи (не более 15 символов):”);

fflush(stdin); fgets(key, 15, stdin);

strcpy(buffer, “”); // очистка буфера данных

printf(“Введите данные (не более 127 символов):”);

fflush(stdin); fgets(buffer, 127, stdin);

// формирование заголовка записи

work.length = strlen(buffer);

work.key_length = strlen(key);

if( *(key + work.key_length – 1) == ‘\n’)

work.key_length--;

work.offset = work.length + work.key_length + sizeof(struct file_record);

work.indicator = ‘B’;

// запись в файл

if(!fwrite(&work, sizeof(struct file_record), 1, fptr))

break;

if(!fwrite(key, work.key_length, 1, fptr))

break;

if(!fwrite(buffer, work.length, 1, fptr))

break;

fflush(stdin);

}

// проверка – был ли выход из цикла по ошибке

……………………………………………………………………

// корректировка общего числа записей в файле

if(in_lines)

{

file_lines += in_lines; f_ptr = ftell(fptr);

freopen(argv[1], “r + b”, fptr);

fwrite(&file_lines, 2, 1, fptr);

fsetpos(&f_ptr); // позиционирование в конец файла, чтобы не было

// потери данных при закрытии файла

fclose(fptr);

}

………………………………………………………………………

}

 

Следующая функция ищет запись по номеру и размещает ее в buffer, если запись найдена. Если запись найдена, то функция возвращает ее номер, иначе возвращает EOF. Нумерация записей выполняется с 0.

int scan_by_number(FILE * fptr, int in_line, char * buffer) // in_line количество записей

// для просмотра

{

extern struct file_record work;

int cur_line;

fops_t f_ptr;

fgetpos(fptr, &f_ptr);

for (cur_line = 0; cur_line <= in_line; cur_line++)

{

fsetpos(fptr, &f_ptr);

// чтение заголовка записи

if(!fread(&work, sizeof(struct file_record), 1, fptr))

return EOF;

f_ptr += work.offset;

}

fseek(fptr, work.key_length, SEEK_CUR); // смещение к данным

if(!fread(buffer, work.length, 1, fptr))

return EOF;

else

return cur_line;

}

 

Поиск данных в файле начинается с текущего положения указателя записей в файле. Считается, что файл к моменту вызова функции – открыт.

Следующая функция ищет запись по ключу. Функция возвращает EOF, если запись не найдена, иначе возвращает номер найденной записи. Данные считываются в буфер, указанный 3-м параметром.

int scan_by_key(FILE *fptr, char *key, char *buffer)

{

extern struct file_record work;

int in_key;

int min_key;

char f_key[KEY_SIZE];

int cur_line = 0;

fops_t f_ptr;

in_key = strlen(key);

if(*(key + in_key-1) == ‘\n’) // символ ‘\n’ в ключе не учитывается

in_key--;

fgetpos(fptr, &f_ptr);

while(!feof(fptr))

{

fsetpos(fptr, &f_ptr);

if(!fread(&work, sizeof(struct file_record), 1, fptr))

return EOF;

min_key = (work.key.length > in_key) ? in_key : work.key_length;

if(!fread(f_key, work.key_length, 1, fptr))

return EOF;

f_key[min_key] = ‘\0’;

strlwr(f_key); // преобразование прописных букв в строчные

strlwr(key);

if(strstr(f_key, key) != NULL) // сравнение хранимых и искомого ключей

{

if(!fread(buffer, work.length, 1, fptr))

return EOF;

return cur_line;

}

// перемещение к следующей записи

f_ptr += work.offset;

cur_line ++;

}

return EOF;

}



Дата добавления: 2016-05-26; просмотров: 1466;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

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

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.023 сек.