Организация произвольного доступа к элементам двоичных файлов.
Организация произвольного доступа к компонентам файла позволяет считывать значения из любой позиции в файле, а также записывать новую информацию в любое место в файле. Но к файлам с произвольным доступом предъявляется одно жесткое требование: их компоненты должны иметь одинаковую длину.
Первичная запись в файл возможна только в режиме последовательного доступа.
Для организации произвольного доступа к элементам файла можно осуществить с помощью функции 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() в двоичном файле.
Пример 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 - экран |