Попробуйте часть кода до слова main заменить на #include "stdafx.h" //1 #include <iostream> //2 using namespace std; //3 |
Работа с файлами
10.1.2. Двоичные файлы
Двоичные файлы отличаются от текстовых тем, что представляют собой последовательность байтов, содержимое которых может иметь различную природу. Это могут быть байты, представляющие числовую информацию в машинном формате, байты с графическими изображениями, байты с аудиоинформацией и т.п. Содержимое таких байтов может случайно совпасть с управляющими кодами таблицы ASCII, но на них нельзя реагировать так, как это делается при обработке текстовой информации. Естественно, что единицей обмена с такими данными могут быть только порции байтов указанной длины.
Создание двоичных файлов с помощью функции fopen отличается от создания текстовых файлов только указанием режима обмена – "rb" (двоичный для чтения), "rb+" (двоичный для чтения и записи), "wb" (двоичный для записи), "wb+" (двоичный для записи и чтения):
FILE *f1; ......... f1=fopen(имя_файла, "режим");
Обычно для обмена с двоичными файлами используются функции fread и fwrite:
c_w = fwrite(buf, size_rec, n_rec, f1);
Здесь
- buf – указатель типа void* на начало буфера в оперативной памяти, из которого информация переписывается в файл;
- size_rec – размер передаваемой порции в байтах;
- n_rec – количество порций, которое должно быть записано в файл;
- f1 – указатель на блок управления файлом;
- c_w – количество порций, которое фактически записалось в файл.
Считывание данных из двоичного файла осуществляется с помощью функции fread с таким же набором параметров:
c_r = fread(buf, size_rec, n_rec, f1);
Здесь
- c_r – количество порций, которое фактически прочиталось из файла;
- buf – указатель типа void* на начало буфера в оперативной памяти, в который информация считывается из файла.
Обратите внимание на значения, возвращаемые функциями fread и fwrite. В какой ситуации количество записываемых порций может не совпасть с количеством записавшихся данных? Как правило, на диске не хватило места, и на такую ошибку надо реагировать. А вот при чтении ситуация, когда количество прочитанных порций не совпадает с количеством запрашиваемых порций, не обязательно является ошибкой. Типичная картина – количество данных в файле не кратно размеру заказанных порций.
Двоичные файлы допускают не только последовательный обмен данными. Так как размеры порций данных и их количество, участвующее в очередном обмене, диктуются программистом, а не смыслом информации, хранящейся в файле, то имеется возможность пропустить часть данных или вернуться повторно к ранее обработанной информации. Контроль за текущей позицией доступных данных в файле осуществляет система с помощью указателя, находящегося в блоке управления файлом. С помощью функции fseek программист имеет возможность переместить этот указатель:
fseek(f1,delta,pos);
Здесь
- f1 – указатель на блок управления файлом;
- delta – величина смещения в байтах, на которую следует переместить указатель файла;
- pos – позиция, от которой производится смещение указателя (0 или SEEK_SET – от начала файла, 1 или SEEK_CUR – от текущей позиции, 2 или SEEK_END – от конца файла)
Кроме набора функций { fopen/fclose, fread/fwrite } для работы с двоичными файлами в библиотеке BC предусмотрены и другие средства – _dos_open /__dos_close, _dos_read /_dos_write, _create /_close, _read /_write. Однако знакомство со всеми возможностями этой библиотеки в рамках настоящего курса не предусмотрено.
Пример 2. Рассмотрим программу, которая создает двоичный файл для записи с именем c_bin и записывает в него 4*10 порций данных в машинном формате (строки, целые и вещественные числа). После записи данных файл закрывается и вновь открывается для чтения. Для демонстрации прямого доступа к данным информация из файла считывается в обратном порядке – с конца. Контроль записываемой и считываемой информации обеспечивается дублированием данных на экране дисплея.
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> main( ) { FILE *f1; //указатель на блок управления файлом int j,k; char s[]="Line"; int n; float r; f1=fopen("c_bin","wb"); //создание двоичного файла для записи for(j=1;j<11;j++) { r=sqrt(j); fwrite(s,sizeof(s),1,f1); //запись строки в файл fwrite(&j,sizeof(int),1,f1); //запись целого числа в файл fwrite(&r,sizeof(float),1,f1); //запись вещественного числа printf("\n%s %d %f",s,j,r); //контрольный вывод } fclose(f1); //закрытие файла printf("\n"); f1=fopen("c_bin","rb"); //открытие двоичного файла для чтения for(j=10; j>0; j--) {//перемещение указателя файла fseek(f1,(j-1)*(sizeof(s)+sizeof(int)+sizeof(float)),SEEK_SET); fread(&s,sizeof(s),1,f1); //чтение строки fread(&n,sizeof(int),1,f1); //чтение целого числа fread(&r,sizeof(float),1,f1); //чтение вещественного числа printf("\n%s %d %f",s,n,r); //контрольный вывод } getch(); return 0; } //=== Результат работы === Line 1 1.000000 Line 2 1.414214 Line 3 1.732051 Line 4 2.000000 Line 5 2.236068 Line 6 2.449490 Line 7 2.645751 Line 8 2.828427 Line 9 3.000000 Line 10 3.162278 Line 10 3.162278 Line 9 3.000000 Line 8 2.828427 Line 7 2.645751 Line 6 2.449490 Line 5 2.236068 Line 4 2.000000 Line 3 1.732051 Line 2 1.414214 Line 1 1.00000010.2.
Использованные в этом примере операторы:
fclose(f1); //закрытие файла f1=fopen("c_bin","rb"); //открытие двоичного файла для чтения
могут быть заменены обращением к единственной функции freopen, которая повторно открывает ранее открытый файл:
f1=freopen("c_bin","rb");
Основное правило, которого надо придерживаться при обмене с двоичными файлами звучит примерно так – как данные записывались в файл, так они должны и читаться.
10.1.3. Структурированные файлы
Структурированный файл является частным случаем двоичного файла, в котором в качестве порции обмена выступает структура языка C, являющаяся точным аналогом записи в Паскале. По сравнению с предыдущим примером использование записей позволяет сократить количество обращений к функциям fread/fwrite, т.к. в одном обращении участвуют все поля записи.
Инициализация структурированного файла выполняется точно таким же способом, как и подготовка к работе двоичного файла.
Пример 3. Приведенная ниже программа является модификацией предыдущего примера. Единственное ее отличие состоит в использовании структуры (записи) b, состоящей из символьного ( b.s, 5 байт, включая нулевой байт – признак конца строки), целочисленного ( b.n, 2 байта в BC и 4 байта в BCB) и вещественного ( b.r, 4 байта) полей.
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <conio.h> main( ) { FILE *f1; int j,k; struct { char s[5]; int n; float r; } b; strcpy(b.s,"Line"); f1=fopen("c_rec","wb"); for(j=1;j<11;j++) { b.n=j; b.r=sqrt(j); fwrite(&b,sizeof(b),1,f1); printf("\n%s %d %f",b.s,b.n,b.r); } fclose(f1); printf("\n"); f1=fopen("c_rec","rb"); for(j=10; j>0; j--) { fseek(f1,(j-1)*sizeof(b),SEEK_SET); fread(&b,sizeof(b),1,f1); printf("\n%s %d %f",b.s,b.n,b.r); } getch(); }
Результат работы этой программы ничем не отличается от предыдущего примера.