Наибольшими возможностями позиционирования указателя потока обладает функция int fseek (FILE *f, long offset, int from). Она сдвигает указатель в файле f на offset байт от положения, указанного третьим параметром from.
Чаще всего from принимает значение SEEK_SET или, что то же самое, нулевое значение. Тогда указатель файла перемещается на offset байт “вперёд” от начала потока в сторону конца файла, если offset>=0. В этом случае offset задаёт порядковый номер байта, на который надо установить указатель чтения-записи. При таком нулевом значении параметра from параметр offset не должен быть отрицательным. Например,
int a; fseek(f, 20, SEEK_SET); a=fgetc(f); // ??????
прочитает информацию, начиная с 20-го байта двоичного файла, который желательно открывать с режимом “rb”.
Если from=SEEK_END, что соответствует целому числу 2,то при неположительном значении параметра offset указатель файла перемещается на |offset| байт “назад” от конца потока в сторону начала файла. При таком значении параметра from параметр offset не должен быть положительным. В противном случае мы “выйдем” за границу файла.
Если from=SEEK_CUR, что соответствует целому числу 1, то можно “продвигаться” на offset байт “вперёд” от текущей позиции указателя при offset>0, или “назад” к началу файла, если offset<0.
Функция fseek() возвращает 0 в случае успешного выполнения и ненулевое значение в случае ошибки.
Пример 1.Прямой доступ к файлу. Прочитать и вывести на экран n-й одномерный массив фиксированной размерности m, (n-2)-й массив, (n-4)-й массив и т. д. до начала файла.
/* Вариант 1. Указатель файла перемещаем, используя в функции fseek () позицию начала файла, т. е. в качестве последнего параметра этой функции используем SEEK_SET */
4.2. Замена записи. Функции ftell, fgetpos, fsetpos, rewind.
В предыдущий проект (п. 4.1) добавим ещё одну функцию, которая в файле одномерных массивов меняет массив с номером nChange. Замена выполняется в следующей последовательности:
void MyChange(int nChange)
{
// nChange — номер массива (записи), который меняем
// 2) Переходим на начало массива с номером nChange
fseek(arf, (nChange-1) * sizeof(a), 0 );
/* 3) Запоминаем номер первого байта начала массива c номером nChange Нумерация байт с начала файла */
long p; p=ftell(arf); // or fgetpos(arf,&p);
cout<<endl<<"p="<<p<<endl; // p=20, если nChange=2
// 4) Читаем массив c номером nChange в оперативную память
fread(a, sizeof(a), 1, arf ) ;
printf("\n Массив, который будем менять");
/* 5) Выводим прочитанный массив и меняем его по заданному алгоритму */
for(j=0;j<m;j++)
{ printf("%d ",a[j]);
a[j]=a[j]*10;
}
/* 6) После выполнения функции fread указатель чтения-записи переместился “вперёд” к концу файла на один массив, т. е. на начало следующего массива с номером nChange+1. Поэтому устанавливаем позицию указателя чтения-записи на начало того же уже прочитанного массива c номером nChange, чтобы на это место записать изменённый массив. В нашем примере для наглядности при выводе каждый элемент увеличили в 10 раз.*/
fsetpos(arf,&p);
/* 7) На место старого массива записываем новый, изменённый массив. */
fwrite(a,sizeof(a),1, arf);
/* Запоминаем номер первого байта массива, следующего после изменённого, т.е. 40, если. nChange=2 Заметим, что в этом простом примере это можно не делать.*/
fgetpos(arf,&p); // или p=ftell(arf);
// Переходим на начало файла для дальнейшего его чтения
/* Чтение файла, а, значит, и переход в его начало можно здесь опустить, так как в предыдущем пункте 4.1 была составлена функция для чтения. Её можно вызвать при необходимости. */
fclose(lf);
}
Пример. В файл записать координаты точек плоскости. Найти две (любые) точки с наибольшим расстоянием между ними. Массив для хранения координат всех точек не использовать.
/* Просмотр файла. Кроме этого, функция находит количество точек, координаты которых записаны в файле. Это количество затем будет передано в функцию поиска MyFind. Поэтому функцию MyRead надо выполнить (вызвать) перед MyFind. */
/* Функция находит две (любые) точки с наибольшим расстоянием между ними. В функцию передаём найденное в предыдущей функции количество точек. Функция возвращает координаты двух найденных точек (переменные типа структуры p1 и p2) и расстояние между ними (dres). */
/* Устанавливаем указатель чтения-записи в позицию, начиная с которой будем читать структуру (координаты одной точки) для сравнения со всеми далее расположенными точками в файле. Первый раз должны читать координаты первой точки. Поэтому pos =0. Далее будем использовать позицию, полученную с помощью функции fgetpos. */
fsetpos(fP,&pos);
fread (&Point1, sizeof(Point1),1,fP);
/* Запоминаем позицию в файле, чтобы, прочитав координаты всех точек, вернуться к этой точке. */
fgetpos(fP,&pos);
/* Читаем координаты точек до конца файла*/
fread (&Point2, sizeof(Point2),1,fP);
while (!feof(fP))
{
/* Находим расстояние от прочитанной точки до ранее фиксированной точки Point1… */
d=MyDist(Point1, Point2);
cout<<d<<" "; // … для контроля выводим его…
// … и сравниваем его с наибольшим расстоянием.
if (d>dres)
// Запоминаем координаты двух точек и расстояние между ними
{dres=d; p1=Point1; p2=Point2;
}
fread (&Point2, sizeof(Point2),1,fP);
}
}
fclose(fP);
}
int main()
{ int flag=1; long n=0; TPoint P1,P2; float D;
while (flag)
{ cout << "\n 1 -- CREATE"<<
"\n 2 -- LOOK"<<
"\n 3 -- FIND"<<
"\n 0 -- EXIT"<<endl;
cin>>flag;
switch (flag)
{ case 1: MyCREATE(); break;
case 2: n=MyREAD(); break;
case 3:
/* Напоминаем, что если не читали файл, то и не получили количество точек в файле. В таком случае необходимо выполнить второй пункт меню */
if (n==0) printf("\n n=0. Do number 2");
else { MyFIND(n,P1,P2, D);
printf("\n Result: x1=%6.1f y1=%6.1f",
P1.x, P1.y);
printf(" x2=%6.1f y2=%6.1f Dist=%8.3f",
P2.x, P2.y, D); break;
case 0: return 0;
}
}
}
Упражнения, тесты.
1. Пусть в программе записано следующее объявление: FILE *f; Что объявлено?
Варианты ответа:
1) указатель на область оперативной памяти для данных, хранящихся в файле на внешнем устройстве;
2) указатель на область оперативной памяти для структуры, поля которой зависят от конкретной задачи;
3) указатель на область оперативной памяти для характеристик (параметров) файла в виде структуры со стандартным именем FILE;
4) указатель на область внешней памяти для характеристик (параметров) файла в виде структуры со стандартным именем FILE;
2. С каким режимом (типом доступа к файлу), который записывается в функции fopen, можно создать новый файл?
Варианты ответа:
1) только “w”; 2) только “w+”; 3) “w”и “w+”; 4) только “a”; 5) только “a+”; 6) “a” и “a+”; 7) “w”, “w+”,“a”и “a+”; 8) “w”и “a”;
Сколько и какие целые числа запишем в файл с помощью одного вызова функции fwrite(a, 20, 2, f); ?
Варианты ответа:
1) 10 первых чисел массива a;
2) 20 чисел: 15 записанных при объявлении массива и пять нулей;
3) 15 записанных при объявлении массива;
4) 10 чисел из второй половины массива а, то есть
110, 12, 13, 14, 15, 0, 0, 0, 0, 0 ;
5) Ошибка в fwrite, так как нет в массиве 20*2=40 чисел, которые функция пытается записать.
6. Как надо объявить массив а с элементами типа short, чтобы с помощью функции fwrite(a, 40, 1, f); весь этот массив был записан в файл?
Варианты ответа:
1) short a[10]; 2) short a[20]; 3) short a[80]; 4) short a[40]; 5) short a[5];
7. Пусть в программе записаны следующие объявления:
FILE *f;
struct ts { char fio[15]; int tel; } vs;
открыт файл (f=fopen(“…”, “w”);) и определены поля структуры.Какие из следующих операторов (последовательностей операторов) правильно записывают одну структуру (ФИО и телефон) в файл?