Произвольный доступ к данным с применением блочного ввода/вывода
Указанный тип доступа обеспечивается функциями, которые позволяют перемещать указатель текущей записи в пределах файла. После перемещения указателя можно применять обычные функции ввода/вывода, в том числе и блочного типа. Прототипы функций размещаются в файле 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;