Наибольшими возможностями позиционирования указателя потока обладает функция 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)-й массив и т. д. до начала файла.
const m=5,
n=8;
FILE *arf;
// Создание файла. Объяснение смотри в 3.2
void MyCr()
{ int a[m];
arf= fopen("d:\\ANA\\cpp\\2004_05\\farr2.dat","wb");
printf("\n");
for (int i=0; i<n;i++)
{ cout<<endl;
for (int j=0;j<m;j++)
{ a[j]=(i+1)*(j+1);
printf("%5d",a[j]);
}
fwrite (a, sizeof(a),1, arf);
}
fclose(arf); cout<<"\nFile was created"; }
// Просмотр файла. Объяснение смотри в 3.2
void MyRead()
{ int a[m];
arf= fopen("d:\\ANA\\cpp\\2004_05\\farr2.dat","rb");
fread(a,sizeof(a),1,arf);
while (!feof(arf))
{
printf("\n");
for (int j=0;j<m;j++)
printf("%5d",a[j]);
fread(a,m*sizeof(int),1,arf);
}
fclose(arf);
}
/* Вариант 1. Указатель файла перемещаем, используя в функции fseek () позицию начала файла, т. е. в качестве последнего параметра этой функции используем SEEK_SET */
void MyRead1()
{ int a[m];
arf= fopen("d:\\ANA\\cpp\\2004_05\\farr2.dat","rb");
for (int i=1; i<=n;i+=2)
{ fseek(arf, (n-i)*sizeof(a),SEEK_SET);
fread(a,sizeof(a),1,arf);
printf("\n");
for (int j=0;j<m;j++)
printf("%5d",a[j]);
}
fclose(arf);
}
/* Вариант 2. Указатель файла перемещаем, используя в функции fseek () позицию конца файла SEEK_END */
void MyRead2()
{ int a[m];
arf= fopen("d:\\ANA\\cpp\\2004_05\\farr2.dat","rb");
for (int i=1; i<=n;i+=2)
{ fseek(arf, -i*sizeof(a),SEEK_END);
fread(a,sizeof(a),1,arf);
printf("\n");
for (int j=0;j<m;j++)
printf("%5d",a[j]);
}
fclose(arf); }
/* Вариант 3. Указатель файла перемещаем, используя в функции fseek () текущую позицию файла SEEK_CUR */
void MyRead3()
{ int a[m];
arf= fopen("d:\\ANA\\cpp\\2004_05\\farr2.dat","rb");
fseek(arf, -1*sizeof(a), SEEK_END);
for (int i=0; i<n/2;i++)
{ fread(a,sizeof(a),1,arf);
printf("\n");
for (int j=0;j<m;j++)
printf("%5d",a[j]);
fseek(arf, -3*sizeof(a),SEEK_CUR);
}
fclose(arf); }
int main()
{ int flag; while (1)
{ cout << "\n1 -- CREATE"<<endl<<
"2 -- READ"<<endl<<
"3 -- Read1"<<endl<<
"4 -- Read2"<<endl<<
"5 -- Read3"<<endl<<
"0 -- EXIT"<<endl;
cin>>flag;
switch (flag)
{ case 1: MyCr(); break;
case 2: MyRead(); break;
case 3: MyRead1(); break;
case 4: MyRead2(); break;
case 5: MyRead3(); break;
case 0: return 0;
}
}
}
4.2. Замена записи. Функции ftell, fgetpos, fsetpos, rewind.
В предыдущий проект (п. 4.1) добавим ещё одну функцию, которая в файле одномерных массивов меняет массив с номером nChange. Замена выполняется в следующей последовательности:
void MyChange(int nChange)
{
// nChange — номер массива (записи), который меняем
// 1) Открываем в режиме "rb+".
arf= fopen("d:\\ANA\\cpp\\2004_05\\farr2.dat ", "rb+");
// 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);
// Переходим на начало файла для дальнейшего его чтения
rewind(arf);
fread(a,sizeof(a),1,lf);
while (!feof(lf))
{ printf("\n"); for(j=0;j<m;j++) printf("%d ",a[j]);
fread(a,sizeof(a),1,lf);
}
/* Чтение файла, а, значит, и переход в его начало можно здесь опустить, так как в предыдущем пункте 4.1 была составлена функция для чтения. Её можно вызвать при необходимости. */
fclose(lf);
}