Как вернуться в начало файла си
Операция чтения-записи всегда производится с текущей позиции в потоке. При открытии потока в режимах r и w указатель текущей позиции устанавливается на начальный байт потока. При открытии в режиме a указатель устанавливается на конец файла сразу за конечным байтом. И при выполнении операции чтения-записи указатель в потоке перемещается на новую позиции в соответствии с числом прочитанных или записанных байтов.
Однако вполне возможно, что нам потребуется считывать или записывать с какой-то определенной позиции в файле. Например, в айдиофайле в формате wav собственно звуковые данные расположены, начиная с 44 байта. И если, к примеру, мы хотим распознать звук из файла, что-то с ним сделать, то нам при считывании данных надо переместить указатель на соответствующую позицию.
В языке Си для управления позицией указателя в потоке применяется функция fseek() , которая имеет следующий синтаксис:
int fseek(указатель_на_поток, смещение, начало_отсчета);
Второй параметр этой функции — смещение представляет числовое значение типа long , которое указывает, на какое количество байт надо переместить указатель. Это значение может быть отрицательным, если необходимо в потоке вернуться назад на некоторое количество байт.
Третий параметр — начало_отсчета задает начальную позицию, относительно которой идет смещение. В качестве этого параметра мы можем использовать одну из встроенных констант, определенных в файле stdio.h :
- SEEK_SET : имеет значение 0 и представляет начало файла
- SEEK_CUR : имеет значение 1 и представляет текущую позицию в потоке
- SEEK_END : имеет значение 2 и представляет конец файла
Если перемещение указателя было успешно выполнено, то функция fseek() возвращает 0, иначе она возвращает ненулевое значение.
Применим функцию fseek в программе:
#include void load(char *, int); void save(char *); int main(void) < // файл для записи и чтения char * filename = "data.txt"; // позиция, с которой начинается считывание int position = 6; save(filename); load(filename, position); return 0; >void load(char * filename, int position) < // буфер для считавания данных из файла char buffer[256]; // чтение из файла FILE *fp = fopen(filename, "r"); if(!fp) < printf("Error occured while opening file\n"); return; >// перемещаем указатель в файле на позицию position fseek(fp, position, SEEK_SET); // пока не дойдем до конца, считываем по 256 байт while((fgets(buffer, 256, fp))) < printf("%s", buffer); >fclose(fp); > void save(char * filename) < // строка для записи char * message = "Hello METANIT.COM!"; // запись в файл FILE *fp = fopen(filename, "w"); if(!fp) < printf("Error occured while opening file\n"); return; >// записываем строку fputs(message, fp); fclose(fp); printf("File has been written\n"); >
Здесь функция save() записывает в файл строку «Hello METANIT.COM!». Далее функция load() считывет данные из этой строки. Но считывает не сначала, а с позиции, которая передается через параметр position :
// перемещаем указатель в файле на позицию position fseek(fp, position, SEEK_SET);
Поскольку третий аргумент равен SEEK_SET , то указатель файла смещается к байту с индексом position. Соответственно далее функция fgets() будет считывать данные не с самого начала файла, а с позиции position:
while((fgets(buffer, 256, fp)))
Поскольку в данном случае в качестве позиции передается число 6, то в тексте файла будут пропущены первые 6 символов, и будет считана подстрока «METANIT.COM!»
ftell
Кроме функции fseek() мы можем использовать для управления позицией указателя еще пару функций:
- long ftell(FILE *) : получает текущую позицию указателя
- void rewind(FILE *) : указатель устанавливается на начало потока
Например, мы можем применять функцию ftell для вычисления длины файла в байтах:
#include int main(void) < FILE* fp = fopen("test.txt", "r"); if(!fp) // если не удалось открыть файл < printf("Error while opening file\n"); return -1; >// если удалось, открыть файл получаем его длину fseek(fp, 0, SEEK_END); // устанавливаем указатель на конец файл long size = ftell(fp); // получаем значение указателя относительно начала fclose(fp); // закрываем файл printf("File size: %ld bytes\n", size); >
В данном случае находим длину файла «text.txt», который располагается в папке программы. Само находждение длины разбивается на два этапа. Сначала помещаем указатель в файле на конец с помощью функции fseek() :
fseek(fp, 0, SEEK_END);
Затем с помощью функции ftell() вычисляем положение указателя относительно начала файла — фактически получаем размер файла в байтах
long size = ftell(fp);
Считывание определенной структуры из файла
Рассмотрим более сложный пример:
#include #include 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[] = < , , , >; int n = sizeof(people) / sizeof(people[0]); save(filename, people, n); load(filename); return 0; > // запись в файл массива структур int save(char * filename, struct person * st, int n) < char *c; // указатель для посимвольной записи данных // число записываемых байтов int size = n * sizeof(struct person); FILE * fp = fopen(filename, "wb"); if (!fp) < printf("Error occured while opening file\n"); return 1; >// записываем количество структур c = (char *)&n; for (int i = 0; i < sizeof(n); 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) < char *c; // указатель на считывания очередного символа int n = sizeof(struct person); // сколько байт надо считать для структуры int index; // номер структуры из файла: printf("Enter user number: "); // ввод номера структуры scanf("%d", &index); FILE * fp = fopen(filename, "rb"); // открываем файл на чтение if (!fp) < printf("Error occured while opening file\n"); return 1; >// выделяем память для количества структур int *ptr_count = malloc(sizeof(int)); // считываем количество структур c = (char *)ptr_count; int m = sizeof(int); // сколько надо считать структур // пока не считаем m байт while (m > 0 && (*c = getc(fp))!=EOF) // сохраняем байт в выделенный блок для размера массива < c++; m--; >//получаем число элементов int count = *ptr_count; free(ptr_count); // освобождаем память // если номер запрощенной структуры меньше кол-ва структур if(index > count) < printf("User number out of range\n"); fclose(fp); return 1; >// получаем, на сколько байтов надо перемотать указатель относительно начала позиции int pos = (index-1) * n + 4; // перемещаем указатель на нужную позицию fseek(fp, pos, SEEK_SET); // выделяем память для считываемой структуры struct person * ptr = malloc(sizeof(struct person)); // устанавливаем указатель на начало блока памяти c = (char *)ptr; // после записи считываем посимвольно из файла while(n > 0 && (*c=getc(fp))!=EOF) < c++; n--; >// вывод считанных данных на консоль printf("%-10s %5d \n", ptr->name, ptr->age); free(ptr); fclose(fp); return 0; >
С помощью функции save в файл сохраняется массив структур. Затем в функции load считываем одну из структур по введенному номеру.
Для поиска нужной структуры вычисляем позицию:
int pos = (index-1) * n + 4;
Исходя из записи мы знаем, что первые 4 байта в файле отводятся под хранение количества структур, соответственно при чтении нам надо пропустить эти 4 байта.
Например, мы хотим получить первую структуру. По формуле получаем (index-1) * n + 4 = 4. То есть первая структура будет располагаться после 4-го байта. Аналогично вторая структура будет располагаться после n+4 байт, где n — это размер структуры.
Получив позицию, передаем ее в функцию fseek() , перемещаемся в потоке и считываем после этого n байт.
// перемещаем указатель на нужную позицию fseek(fp, pos, SEEK_SET);
При последующей операции чтения с помощью функции getc() :
while(n > 0 && (*c=getc(fp))!=EOF)
эта функция будет считывать данные с позиции pos , по которой располагается структура с номером index.
Результат работы программы:
Input user number: 2 Alice 27
Стоит отметить, что для большей краткости здесь убрана проверка на наличие структуры по введенному номеру, так как пользователь может ввести число, которое превышает количество
Обновление структуры в файле
Мы можем использовать указатель в потоке не только для чтения, но и для записи данных. Например:
#include struct person < unsigned id; char name[10]; int age; >; void save(char*); void update_age(char*, int, int); int main() < char * filename = "people.bin"; save(filename); update_age(filename, 2, 33); // в структуре с устанавливаем age = 33 >void save(char* filename) < struct person people[] = < , , >; int size = sizeof(people[0]); // размер всего массива int count = sizeof(people) / size; // количество структур // запись файла FILE *fp = fopen(filename, "w"); // записываем массив структур fwrite(people, size, count, fp); fclose(fp); printf("%d people saved\n", count); > void update_age(char* filename, int id, int age) < // считывание файла, пока не найдем структуру с определенным id struct person p; // структура для чтения int size = sizeof(p); FILE* fp = fopen(filename, "r+"); // "r+" - открываем файл для изменения // считываем данные в структуру while(fread(&p, sizeof(p), 1, fp)==1) < // если нашли структуру с нужным id if(p.id == id) < p.age = age; // изменяем возраст fseek(fp, -1*size, SEEK_CUR); // перемещаем в потоке на один объект назад fwrite(&p, size, 1, fp); // записываем обновленную структуру break; // выходим из цикла >> rewind(fp); // перематываем файл назад // считываем по одной структуре и проверяем изменения while(fread(&p, sizeof(p), 1, fp) == 1) < printf("Id: %d \t Name: %s \t Age: %d \n", p.id, p.name, p.age); >fclose(fp); >
В функции update_age() обновляем возраст пользователя по определенному id. Для этого открываем файл с флагом «r+», то есть для чтения с измением.
FILE* fp = fopen(filename, "r+");
Затем считываем по одной структуре и проверяем id. Если id структуры равен запрошенному id, то изменяем возраст и перезаписываем структуру в файле:
if(p.id == id) < p.age = age; // изменяем возраст fseek(fp, -1*size, SEEK_CUR); // перемещаем в потоке на один объект назад fwrite(&p, size, 1, fp); // записываем обновленную структуру break; // выходим из цикла >
Для перезаписи перемещаемся в файле на начало структуры ( fseek(fp, -1*size, SEEK_CUR) ). Поскольку, когда мы считали структуру, указатель в файле указывает на следующую за ней структуру.
Поскольку id у структур уникальны, соответственно нам надо изменить только один объект, поэтому после изменения объекта выходим из цикла.
Чтобы до закрытия файла вывести в этой же функции все структуры из файла, перемещаем указатель в файле на начало с помощью функции rewind()
rewind(fp); // перематываем файл назад
Таким образом, структура с где изначально age был равен 27, изменит это значение age на 33. Консольный вывод программы:
3 people saved Id: 1 Name: Tom Age: 38 Id: 2 Name: Sam Age: 33 Id: 3 Name: Bob Age: 42
rewind
Однако, в отличие от fseek этого, rewind очищает индикаторы ошибок для потока и индикатора конца файла. Кроме того, в отличие от fseek этого, rewind не возвращает значение, указывающее, был ли указатель успешно перемещен.
Чтобы очистить буфер клавиатуры, используйте rewind поток stdin , связанный с клавиатурой по умолчанию.
Если поток является NULL указателем, вызывается обработчик недопустимых параметров, как описано в разделе «Проверка параметров». Если выполнение разрешено продолжать, эта функция возвращается и errno имеет значение EINVAL .
Дополнительные сведения об этих и других кодах ошибок см. в разделе errno , _doserrno _sys_errlist и _sys_nerr .
По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это поведение, см . статью «Глобальное состояние» в CRT.
Требования
| Маршрут | Обязательный заголовок |
|---|---|
| rewind |
Дополнительные сведения о совместимости см. в разделе Совместимость.
Библиотеки
Пример
// crt_rewind.c /* This program first opens a file named * crt_rewind.out for input and output and writes two * integers to the file. Next, it uses rewind to * reposition the file pointer to the beginning of * the file and reads the data back in. */ #include int main( void ) < FILE *stream; int data1, data2; data1 = 1; data2 = -37; fopen_s( &stream, "crt_rewind.out", "w+" ); if( stream != NULL ) < fprintf( stream, "%d %d", data1, data2 ); printf( "The values written are: %d and %d\n", data1, data2 ); rewind( stream ); fscanf_s( stream, "%d %d", &data1, &data2 ); printf( "The values read are: %d and %d\n", data1, data2 ); fclose( stream ); >>
Выходные данные
The values written are: 1 and -37 The values read are: 1 and -37
Как перейти в начало текстового файла?

Какую строку добавить в код, чтоб перейти в начало файла?
1 2 3 4 5 6
while (fin.get(ch)) { if (isdigit(ch)) ++num; }
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Как в начало каждой строки каждого текстового файла в текущем каталоге вставить имя этого же файла?
Здравствуйте, подскажите пожалуйста как в текущем каталоге, в начало каждой строки каждого.

Как добавить строку в начало текстового файла
Добрый день! Столкнулся с такой проблемой, вернее проблемкой. При выводе отчетных данных в.
Как найти начало и конец строки файла (текстового)
Здравствуйте! Как найти начало и конец строки файла (текстового). Спасибо
Как в начало каждой строки текстового файла вставить заданное слово?
как в txt в каждую строку перед первым словом в строке написать слово mod? пустых строк нет .
int fseek (FILE *stream, long offset, int origin)
Функция fseek() устанавливает указатель положения в файле, связанном со stream, в соответствии со значениями offset и origin. Ее основное назначение — поддерживать операции ввода/ вывода по произвольному адресу. Аргумент offset — это выраженный в байтах сдвиг от позиции, определяемой origin, до новой позиции. Аргумент origin может принимать значения 0, 1 или 2, причем 0 означает начало файла, 1 — текущую позицию, а 2 — конец файла. В stdio.h определены следующие макросы для origin
| Имя | Позиция origin |
|---|---|
| SEEK_SET | Начало файла |
| SEEK_CUR | Текущая позиция |
| SEEK_END | Конец файла |
В случае успеха fseek() возвращает 0. Ненулевое значение означает неудачу. С помощью fseek() можно переместить указатель положения в любую точку внутри файла и даже за его пределы после конца файла. Однако попытка установить указатель перед началом файла будет воспринята как ошибка.
Функция fseek() сбрасывает флаг конца файла, связанный с указанным потоком. Кроме того, она обнуляет любую предыдущую ungetc() в том же потоке.