Позиционирование в потоке
Операция чтения-записи всегда производится с текущей позиции в потоке. При открытии потока в режимах "r" и "w" указатель текущей позиции устанавливается на начальный байт потока. При открытии в режиме "a" указатель устанавливается на конец файла сразу за конечным байтом. И при выполнении операции чтения-записи указатель в потоке перемещается на новую позиции в соответствии с числом прочитанных или записанных байтов.
Однако вполне возможно, что нам потребуется считывать или записывать с какой-то определенной позиции в файле. Например, в айдиофайле в формате wav собственно звуковые данные расположены, начиная с 44 байта. И если, к примеру, мы хотим распознать звук из файла, что-то с ним сделать, то нам при считывании данных надо переместить указатель на соответствующую позицию.
В языке Си для управления позицией указателя в потоке применяется функция fseek(), которая имеет следующий прототип:
1
int fseek(указатель_на_поток, смещение, начало_отсчета);
Второй параметр этой функции - смещение представляет числовое значение типа long, которое указывает, на какое количество байт надо переместить указатель. Это значение может быть отрицательным, если необходимо в потоке вернуться назад на некоторое количество байт.
Третий параметр - начало_отсчета задает начальную позицию, относительно которой идет смещение. В качестве этого параметра мы можем использовать одну из встроенных констант, определенных в файле stdio.h:
SEEK_SET: имеет значение 0 и представляет начало файла
SEEK_CUR: имеет значение 1 и представляет текущую позицию в потоке
SEEK_END: имеет значение 2 и представляет конец файла
Если перемещение указателя было успешно выполнено, то функция fseek() возвращает 0, иначе она возвращает ненулевое значение.
Кроме функции fseek() мы можем использовать для управления позицией указателя еще пару функций:
long ftell(FILE *): получает текущую позицию указателя
void rewind(FILE *): указатель устанавливается на начало потока
Применим функцию fseek в программе:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <stdio.h>
#include <stdlib.h>
struct person
{
char name[20];
int age;
};
int save(char * filename, struct person *st, int n);
int load(char * filename);
int main(void)
{
char * filename = "people.dat";
struct person people[] = { "Tom", 23, "Alice", 27, "Bob", 31, "Kate", 29 };
int n = sizeof(people) / sizeof(people[0]);
save(filename, people, n);
load(filename);
return 0;
}
// запись в файл массива структур
int save(char * filename, struct person * st, int n)
{
FILE * fp;
char *c;
// число записываемых байтов
int size = n * sizeof(struct person);
if ((fp = fopen(filename, "wb")) == NULL)
{
perror("Error occured while opening file");
return 1;
}
// записываем количество структур
c = (char *)&n;
for (int i = 0; i<sizeof(int); i++)
{
putc(*c++, fp);
}
// посимвольно записываем в файл все структуры
c = (char *)st;
for (int i = 0; i < size; i++)
{
putc(*c, fp);
c++;
}
fclose(fp);
return 0;
}
// загрузка из файла массива структур
int load(char * filename)
{
FILE * fp;
char *c;
int n = sizeof(struct person);
// считываем структуру по определенному индексу из файла:
int i;
// ввод номера структуры
printf("Input user number: ");
scanf("%d", &i);
// получаем, на сколько байтов надо перемотать указатель относительно начала позиции
int pos = (i-1) * n + 4;
if ((fp = fopen(filename, "rb")) == NULL)
{
perror("Error occured while opening file");
return 1;
}
// перемещаем указатель на нужную позицию
fseek(fp, pos, SEEK_SET);
// выделяем память для считываемой структуры
struct person * ptr = (struct person *) malloc(sizeof(struct person));
c = (char *)ptr;
// после записи считываем посимвольно из файла
while(n>0)
{
i= getc(fp);
if(i==EOF) break;
*c = i;
c++;
n--;
}
// вывод считанных данных на консоль
printf("%-20s %5d \n", ptr->name, ptr->age);
free(ptr);
fclose(fp);
return 0;
}
С помощью функции save в файл сохраняется массив структур. Затем в функции load считываем одну из структур по введенному номеру.
Для поиска нужной структуры вычисляем позицию:
1
int pos = (i-1) * n + 4;
Исходя из записи мы знаем, что первые 4 байта в файле отводятся под хранение количества структур, соответственно при чтении нам надо пропустить эти 4 байта.
Например, мы хотим получить первую структуру. По формуле получаем (1-1) * n + 4 = 4. То есть первая структура будет располагаться после 4-го байта. Аналогично вторая структура будет располагаться после n+4 байт, где n - это размер структуры.
Получив позицию, передаем ее в функцию fseek, перемещаемся в потоке и считываем после этого n байт.
Результат работы программы:
Input user number: 2
Alice 27