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

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

Первичная запись в файл возможна только в режиме последовательного доступа.

Для организации произвольного доступа к элементам файла можно осуществить с помощью функции fseek(), прототип которой описан в заголовочном файле <stdio.h>. Синтаксическое описание функции следующее:

int fseek ( указатель_потока, число_перемещаемых_байтов, указатель);

где указатель_потока – указатель на открытый файловый поток, аналогичный возвращаемому функцией fopen();

число_перемещаемых_байтов – число байтов, на которое нужно переместить файловый указатель в направлении, указанном параметром указатель. Для перемещения файлового указателя в обратном направлении (в сторону начала файла) следует устанавливать число_перемещаемых_байтов равным отрицательному значению;

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

Функция fseek() перемещает внутренний указатель файлового потока, изменяя таким образом место в файле, с которого начинается следующая операция чтения или записи. В случае успешного завершения функция возвращает 0, а в случае ошибки – ненулевое значение.

Таблица 4. Значения аргумента «указатель».

Значение Описание
SEEK_SET Перемещение файлового указателя происходит относительно начала файла
SEEK_END Перемещение файлового указателя происходит относительно конца файла
SEEK_CUR Перемещение файлового указателя происходит относительно текущей позиции файлового указателя.

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

Продемонтрируем возможности функции fseеk():

fseek(f, sizeof(t), SEEK_CUR); /* перемещает файловый указатель с

текущей позиции на следующую */

fseek(f, -sizeof(t), SEEK_CUR); /* перемещает файловый указатель с

текущей позиции на предыдущую */

fseek(f, 0, SEEK_END); /* перемещает файловый указатель на конец

файла */

fseek(f,(long)sizeof(a),SEEK_SET); /*перемещение вперед от начала на длину переменной а */fseek(f,-(long)sizeof(a),SEEK_CUR); /*перемещение назад от текущей позиции на длину переменной а*/

При организации произвольного доступа используется функция ftell(), осуществляющая навигацию внутри файла. Прототип функции описан в <stdio.h>. Данная функция возвращает внутренний указатель файлового потока, равный смещению в байтах от начала двоичного файла до байта, с которого начинается следующая операция ввода/вывода. Это значение можно передать функции fseek() или использовать каким-либо другим образом. Единственным аргументом функции ftell() является указатель на файловый поток:

long int ftell( указатель_потока);

Кроме этой функции, для прямого доступа к файлу используется функция

void rewind(FILE *f);

которая устанавливает значение указателя на начало потока.

Пример 1. Использование в программе позиционирования файлового указателя с помощью функции fseek() в двоичном файле.

#include "stdafx.h"#include <iostream>using namespace std;#include <io.h>int _tmain(int argc, _TCHAR* argv[]){ FILE *f;//указатель на двоичный файл int i,n=10; char s[]="String"; float r; f=fopen("file_bin","wb"); //создание двоичного файла для записи for(i=1;i<=n;i++){ r=pow(i,1.0/3); fwrite(s,sizeof(s),1,f); //запись строки String в файл fwrite(&i,sizeof(int),1,f); //запись целого числа (номера строки) в файл fwrite(&r,sizeof(float),1,f); //запись вещественного числа (корня кубического) в файл printf("\n%s %d %f",s,i,r);//контрольный вывод на экран } fclose(f);//закрытие файла printf("\n"); f=fopen("file_bin","rb"); //открытие двоичного файла для чтения for(i=n; i>0; i--) { //перемещение указателя файла fseek(f,(i-1)*(sizeof(s)+sizeof(int)+sizeof(float)),SEEK_SET); fread(&s,sizeof(s),1,f);//чтение строки fread(&n,sizeof(int),1,f);//чтение целого числа fread(&r,sizeof(float),1,f); //чтение вещественного числа printf("\n%s %d %f",s,n,r); //вывод на экран содержимого файла } system("pause"); return 0;}

Пример 2: прочитать из файла, содержащего целые числа, его 25-й элемент.

#include <stdio.h>

#include <stdlib.h>

void main(){

FILE *f_inp;

int value;

f_inp = fopen(“Fil.dan”, ”rb) // чтение в двоичном режиме

if(!f_inp) // если указатель равен 0 (не связался)

{puts(“Нельзя открыть файл!\n”);

exit(1);

}

fseek (f_inp, 25 * sizeof(int), SEEK_SET);

fread(value, sizeof(int), 1, f_inp);

printf(“Прочитанное число = %d\n”, value);

fclose(f_inp);

}

В любом случае при работе с двоичными файлами не следует забывать добавлять букву «b» при указании режима доступа функции fopen(), но нужно быть особенно внимательным, если предполагается, что будет осуществляться произвольный доступ к компонентам данного файла.

Пример 3: записать нуль на место максимального значения в файле Test.dat

#include <stdio.h>

#include <stdlib.h>

void main(){

FILE *f_test;

int nom_max; // номер мах элемента

int descriptor; // дескриптор файла

int value, max;

randomize();

f_test = fopen(“Test.dat”, ”r+b) // чтение и дозапись в

// двоичном режиме

if(!f_test) // если указатель равен 0 (не связался)

{puts(“Нельзя открыть файл!\n”);

exit(1);

}

descriptor = fileno(f_test); // преобразовать открытый файловый

// поток в дескриптор файла

fread(&max, sizeof(int), 1, f_test);

nom_max=0

for(int i=1; i<filelength(descriptor); i++)

{fread(&value, sizeof(int), 1, f_test);

if(value > max)

{nom_max=i;

max = value;

}

}

fseek(f_test, nom_max * sizeof(int), SEEK_SET);

fwrite(&zero, sizeof(int), 1, f_test);

fclose(f_test);

}

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

Потоковая обработка файлов.

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

Поток ifstream служит для работы с файлами в режиме чтения.

Поток ofstream служит для работы с файлами в режиме записи.

Поток fstream служит для работы с файлами в режиме, как чтения, так и записи.

В программах на C++ при работе с текстовыми файлами в потоковом режиме необходимо подключать библиотеки <iostream> и <fstream>.

С++ поддерживает всю систему ввода/вывода С и добавляет к ней дополнительные возможности, связанные в основном с вводом/выводом объектов. Описание средств для создания потоков в С++ представлено в заголовочном файле <iostream.h>. Когда начинает работать программа на языке С++, открываются потоки, приведенные в табл.5.

Таблица 5. Потоки, определяемые в языке С++.

Имя стандартного файла Описание
cin Стандартный ввод - клавиатура
cout Стандартный вывод - экран
cerr Стандартная ошибка - экран
clog Буферизованная версия cerr - экран

Наши рекомендации