Перейти к содержимому

Как вернуться в начало файла си

  • автор:

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 

Как вернуться в начало файла си

Операция чтения-записи всегда производится с текущей позиции в потоке. При открытии потока в режимах 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

fseek , _fseeki64

Перемещает файловый указатель в указанное местоположение.

Синтаксис

int fseek( FILE *stream, long offset, int origin ); int _fseeki64( FILE *stream, __int64 offset, int origin ); 

Параметры

stream
Указатель на структуру FILE .

offset
Количество байт начиная с origin .

origin
Первоначальная позиция.

Возвращаемое значение

Если операция завершилась удачно, fseek и _fseeki64 возвращают 0. В противном случае возвращается ненулевое значение. Для устройств, которые не поддерживают поиск, возвращаемое значение не определено. Если stream имеет значение NULL или origin не является одним из разрешенных значений, описанных ниже, fseek и _fseeki64 вызовите обработчик недопустимых параметров, как описано в разделе проверки параметров. Если продолжение выполнения разрешено, эти функции устанавливают для errno значение EINVAL и возвращают -1.

Замечания

И fseek функции перемещают указатель файла (при наличии), связанный с stream новым расположением, offset из байтов origin . _fseeki64 Следующая операция в потоке происходит в новом местоположении. В потоке, открытом для обновления, следующая операция может быть либо операцией чтения, либо операцией записи. Аргумент origin должен быть одним из следующих констант, определенных в STDIO.H :

Значение источника Значение
SEEK_CUR Текущая позиция файлового указателя.
SEEK_END Конец файла.
SEEK_SET Начало файла.

С помощью функций fseek и _fseeki64 можно переместить указатель в любое место в файле. Указатель также может быть размещен за пределами файла. fseek и _fseeki64 очищает индикатор конца файла и отрицает влияние любых предыдущих ungetc вызовов. stream

Когда файл открыт для добавления данных, текущая позиция в файле определяется последней операцией ввода-вывода, а не тем, где должна произойти следующая запись. Если в открытом для добавления файле еще не было ни одной операции ввода-вывода, этой позицией является начало файла.

Для потоков, открытых в текстовом режиме, и _fseeki64 имеют ограниченное использование, fseek так как переводы веб-канала возврата каретки могут вызывать fseek и _fseeki64 создавать непредвиденные результаты. Единственными fseek и _fseeki64 операциями, гарантированно работающими в потоках, открытых в текстовом режиме, являются:

  • поиск со смещением 0 относительно любого из значений origin;
  • Поиск с начала файла со значением смещения, возвращаемым из вызова ftell при использовании fseek или _ftelli64 при использовании _fseeki64 .

Кроме того, в текстовом режиме при вводе CTRL+Z интерпретируется как символ конца файла. В файлах, открытых для чтения/записи, функция fopen и все связанные с ней подпрограммы проверяют наличие символа CTRL+Z в конце файла и удаляют его, если это возможно. Он удаляется из-за того, что при использовании сочетания fseek и ftell (или _fseeki64 ) _ftelli64 перемещение в файл, заканчивающийся сочетанием КЛАВИШ CTRL+Z, может привести fseek к _fseeki64 неправильному ведении в конце файла.

Когда CRT открывает файл, начинающийся с метки порядка байтов (BOM), указатель на файл размещается после BOM. (То есть он расположен в начале фактического содержимого файла). Если вы должны быть fseek в начале файла, используйте ftell для получения начальной позиции, а затем fseek для этой позиции, а не для позиции 0.

Эта функция блокирует работу других потоков во время выполнения, поэтому она потокобезопасна. Сведения о версии, отличной от блокировки, см. в статье _fseek_nolock . _fseeki64_nolock

По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это поведение, см . статью «Глобальное состояние» в CRT.

Требования

Функция Обязательный заголовок
fseek
_fseeki64

Дополнительные сведения о совместимости см. в разделе Совместимость.

Пример

// crt_fseek.c // This program opens the file FSEEK.OUT and // moves the pointer to the file's beginning. #include int main( void ) < FILE *stream; char line[81]; int result; if ( fopen_s( &stream, "fseek.out", "w+" ) != 0 ) < printf( "The file fseek.out was not opened\n" ); return -1; >fprintf( stream, "The fseek begins here: " "This is the file 'fseek.out'.\n" ); result = fseek( stream, 23L, SEEK_SET); if( result ) perror( "Fseek failed" ); else < printf( "File pointer is set to middle of first line.\n" ); fgets( line, 80, stream ); printf( "%s", line ); >fclose( stream ); > 
File pointer is set to middle of first line. This is the file 'fseek.out'. 

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() в том же потоке.

Приведенная здесь функция отыскивает указанную структуру типа addr. Обратим внимание на то , что sizeof используется как для получения количества байт , на которое нужно сместиться , так и для обеспечения переносимости.
struct addr {
char name [ 40 ] ;
char street [ 40 ] ;
char city [ 40 ] ;
char state [ 3 ] ;
char zip [ 10 ] ;
} info ;
void find ( long client_num )
{
FILE * fp ;
if ( ( fp = fopen ( «mail» , «rb» ) ) == NULL ) {
printf ( «Cannot open file. \n » ) ;
exit ( 1 ) ;
}
/* получение правильной структуры */
fseek ( client_num * sizeof ( struct addr ) , 0 ) ;
/* чтение данных в память */
fread ( & info , sizeof ( struct addr ) , 1 , fp ) ;
fclose ( fp ) ;
}

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *