Порядок выполнения лабораторной работы

Лабораторная работа №4

Работа с файловыми потоками в С++.
Двоичные файлы

Цель работы: получить практические навыки решения задач с использованием двоичных файлов на языке С++ с использованием принципов объектно-ориентированного и визуального программирования.

 

Порядок выполнения лабораторной работы

1. Изучить теоретические сведения. Разобрать представленные примеры. Запустить первый, второй и пятый примеры, продемонстрировать их преподавателю, если примеры не были показаны на занятии, результаты их работы должны быть включены в отчет.

2. Получить вариант индивидуального задания у преподавателя. Предполается реализация двух задач: первая задача представлена в пункте лабораторное задание, вторая повторяется из предыдущей работы (вторая группа заданий), только теперь необходима реализация данной задачи при помощи двоичных файлов (объявить структуру и записывать/читать ее из двоичного файла). При необходимости перегрузить операторы << и >> для работы со стандартными потоковыми классами.

3. Реализовать полученные задания. Для выбора решения задачи пользователем реализовать меню. Любое общение с пользователем должно сопровождаться поясняющимся текстом. Также в программе должно быть реализовано минимум 2 исключительных ситуации. Исключительные ситуации должны обрабатываться в отдельном классе. Обязательна оконная (формочная) реализация программы с использованием элементов управления.

4. Показать результат работы программы преподавателю.

5. Защитить лабораторную работу.

Требования к отчету

Отчет должен содержать:

1. Цель работы.

2. Задание.

3. Словесное описание исключительных ситуаций.

4. UML диаграмма классов.

5. Блок-схема программы.

6. Текст программы на языке C++ с комментариями.

7. Тесты (копии выходных форм программы/сформированных файлов).

8. Выводы.

 

Теоретические сведения

Cохранение данных в двоичных файлах

Сохранение в двоичных файлах данных стандартных типов.

Для того, чтобы открыть двоичный файл, необходимо задать режим доступа ios::binary (в некоторых компиляторах С++ - ios::bin).

Двоичные файлы более компактны и в некоторых случаях более удобны для обработки.

Для создания выходного файла создают объект

ofstream out_fil (”Outfil.dat”,ios::out | ios::binary);

if (! out_fil) { cerr<<”Error: Outfil.dat”<<endl;

exit(1);

}

Для того, чтобы открыть существующий двоичный файл для чтения, нужно создать объект

ifstream in_fil (”Infil.dat”, ios::in | ios::binary);

if (! in_fil) { cerr<<”Error: Infil.dat”<<endl;

exit(2);

}

К сожалению, созданные объекты in_fil и out_fil не слишком приспособлены для работы с двоичными файлами и требуют некоторых дополнительных действий, необходимых для корректной работы.

 

Пример 1. Запись значения типа double в двоичный файл.

 

# include <fstream>

# include <iostream>

# include <stdlib.h>

using namespace std;

 

class bin_outstream :public ofstream

{

public:

bin_outstream(const char *fn) : ofstream(fn, ios::out | ios::binary){}

void writeOurDate(const void*, int);

ofstream &operator<<(double d) {

writeOurDate(&d, sizeof(d));

return *this;

}

};

 

int main()

{

bin_outstream bin_out("B_out.dat");

if (!bin_out)

{

cerr << "Unable to write to B_out.dat" << endl;

system("pause");

exit(1);

}

double d = 5.252;

bin_out << d;

bin_out << d*d;

d = 5.2E-5;

bin_out << d;

system("pause");

return 0;

}

 

void bin_outstream::writeOurDate(const void *Ptr, int len)

{

if (!Ptr) return;

if (len <= 0) return;

write((char*)Ptr, len);

}

Пример 2. Чтение значений типа double из двоичного файла.

 

# include <fstream>

# include <iostream>

# include <stdlib.h>

 

using namespace std;

 

class bin_instream : public ifstream

{

public:

bin_instream(const char *fn) : ifstream(fn, ios::in | ios::binary){}

void readOurDate(void*, int);

 

bin_instream &operator>>(double &d) {

readOurDate(&d, sizeof(d));

return *this;

}

};

 

int main()

{

bin_instream bin_in("B_in.dat");

if (!bin_in)

{

cerr << "Unable to open B_in.dat" << endl;

system("pause");

exit(1);

}

double d;

long count = 0;

bin_in >> d;

while (!bin_in.eof())

{

cout << ++count << ':' << d << endl;

bin_in >> d;

}

system("pause");

return 0;

}

 

void bin_instream::readOurDate(void *p, int len)

{

if (!p) return;

if (len <= 0) return;

read((char*)p, len);

}

 

Для работы с файловыми потоками любого из стандартных типов, нужно перегрузить операторы ввода и вывода под требуемый тип данных или воспользоваться шаблоном класса, задаваемым с помощью ключевого слова template.

Сохранение в двоичных файлах данных, имеющих тип, создаваемый пользователем.

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

 

Пример 3.

Объявим структуру

struct mountine {

char name[20]; //название горы

int altitude; //высота над уровнем моря

int complicate; //сложность

};

mountine mount;

Для сохранения информации в двоичном файле выполняют следующее:

ofstream fil_out(”mountines.txt”, ios_base::app);

fil_out << mount.name << ” ” << mount.altitude << ’ ’ << mount.complicate <<”\n”;

Для сохранения той же информации в двоичном файле выполняют следующее:

ofstrem fil_out(”mountines.dat”, ios_base::app | ios_base::bynary);

fil_out.write((char *) &mount, sizeof(mountine));

Метод write копирует указанное число байтов (в данном случае – sizeof(mountine)) в файл из памяти ЭВМ. Несмотря на то, что сохранение данных происходит в двоичном файле, адрес переменной преобразуется к указателю на тип char.

Для чтения данных из двоичного файла используют метод read:

ifstream fil_in(”mountines.dat”, ios_base::binary);

fil_in.read((char *) &mount, sizeof(mountine));

При записи или чтении классов, не содержащих виртуальных функций, можно использовать тот же самый подход. Чтобы сделать класс потоковым, нужно перегрузить операторы << и >>:

friend ostream &operator<<(ostream &, AnyClass &);

friend istream &operator>>(istream &, AnyClass &);

Пример 4. Чтение и запись структуры данных в файл.

//перечисляемый тип с оценками

enum mark {very_bad = 1, bad = 2, satisfactory = 3, good = 4, excellent = 5};

struct rec //структура данных

{ char name[10];

char subname[15];

int clas;

mark marks[4];

};

//открытие "типизированного" файла для записи

void WriteFile(int n, rec &wrec)

{ FILE * fo;

fo = fopen("type.txt","wb"); //открываем (создаем) файл для записи

if (fo == NULL)

{ cout << "Ошибка открытия файла для записи" << endl;

exit(EXIT_FAILURE);

}

for(int i = 0; i < n; i++)

{ int temp=0; //временная переменная для оценок

cout << "Введите имя ученика: "; cin >> wrec.name;

cout << "Введите фамилию ученика: "; cin >> wrec.subname;

cout << "Введите класс, где обучается ученик: "; cin >> wrec.clas;

cout << "Введите оценки ученика (4 штуки от 1 до 5)" << endl;

for(int j = 0; j < 4; j++)

{ cout << "Введите " << j+1 << " оценку: "; cin >> temp;

wrec.marks[j] = (mark)temp;

}

WriteElement(wrec, fo); //запись одной структуры в файл

cout << endl;

}

fclose(fo); //закрываем файл

cout << endl;

}

//запись структуры в файл

void WriteElement(rec &wrec, FILE * f1)

{

fwrite(&wrec,sizeof(rec),1,f1); //записываем одну структуру

}

//чтение структуры из файла

void ReadElement(rec & rrec, FILE * f1)

{

fread(&rrec, sizeof(rec), 1, f1); //читаем одну структуру

}