Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 25.11.2008 | Доступ: свободный | Студентов: 9602 / 1297 | Оценка: 4.06 / 3.66 | Длительность: 21:16:00
Лекция 11:

Работа с файлами

< Лекция 10 || Лекция 11: 1234 || Лекция 12 >

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.000000
10.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();
}

Результат работы этой программы ничем не отличается от предыдущего примера.

< Лекция 10 || Лекция 11: 1234 || Лекция 12 >
Alexey Ku
Alexey Ku

Попробуйте часть кода до слова main заменить на 

#include "stdafx.h" //1

#include <iostream> //2
#include <conio.h>

using namespace std; //3

Александр Талеев
Александр Талеев

#include <iostream.h>
#include <conio.h>
int main(void)
{
int a,b,max;
cout << "a=5";
cin >> a;
cout <<"b=3";
cin >> b;
if(a>b) max=a;
else max=b;
cout <<" max="<<max;
getch();
return 0;
}

при запуске в visual express выдает ошибки 

Ошибка    1    error C1083: Не удается открыть файл включение: iostream.h: No such file or directory    c:\users\саня\documents\visual studio 2012\projects\проект3\проект3\исходный код.cpp    1    1    Проект3

    2    IntelliSense: не удается открыть источник файл "iostream.h"    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    1    1    Проект3

    3    IntelliSense: идентификатор "cout" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    6    1    Проект3

    4    IntelliSense: идентификатор "cin" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    7    1    Проект3

при создании файла я выбрал пустой проект. Может нужно было выбрать консольное приложение?