Казахстан |
Практика работы в среде визуального программирования
Сериализация. Работа с файлами
Сериализация объектов класса CString
Сериализация - процесс записи (чтения) объектовов и данных на диск (с диска). Рассмотрим сериализацию как встроенных классов Visual C++ (например, CString ), так и нестандартных. Если в программе отсутствует документ, на который можно возложить выполнение файловых операций (программы на базе диалоговых окон), то работают с классом MFC CFile. Рассмотрим программу Writer, которая будет записывать на диск введенную строку, а затем, по требованию пользователя, загружать ее из файла.
- Создатим SDI программу Writer.
- Создадим объект CString StringData и инициализируем ее.
- Создадим обаботчик WM_CHAR OnChar().
void CWriterView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { //TODO: CWriterDoc *pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->StringData += char(nChar); Invalidate(); }
Отобразим данные в OnDraw:
pDC->TextOut(0,0,pDoc->StringData);
Класс документа (файл WriterDoc.cpp) содержит встроенный метод Serialize(CArchive& ar) . В этом методе происходит сериализация объекта StringData. Методу Serialize(CArchive& ar) передается ссылка на объект ar класса CArchive. Работа с объектом ar практически не отличается от работы с потоками cout и cin.
if(ar.IsStoring()) ar<<StringData; else ar>>StringData;
Чтобы сообщить приложению об изменении данных (заносим новый символ), вызовем в OnChar(...) метод объекта документа SetModifiedFlag(). Если будет сделана попытка выхода из программы без сохранения данных, то приложение выведет диалоговое окно с предложением сохранить данные.
Добавим строку в метод OnChar(...): Д
pDoc->SetModifiedFlag();
При создании нового документа следует стереть старое содержимое StringData и обновить вид программы новыми данными документа.
Метод OnNewDocument:
BOOL CWriterDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // TODO: add reinitialization code here // (SDI documents will reuse this document) StringData=""; UpdateAllViews(NULL); return TRUE; }
Сериализация нестандартных объектов
Создадим пользовательский класс и организуем сериализацию его объектов. Пусть класс содержит строковую переменную (типа CString ). Кроме этого в классе должен быть реализован конструктор, три метода для работы со строками: AddText() - добавление текста в конец строки, DrawText() - вывод текста в констексте устройства и ClearText() - очистка (стирание содержимого) строки .
Алгоритм организации сериализации нестандартных объектов
- Создать SDI программу Serializer.
- Добавить в проект заголовочный файл.
- Включить в этот файл описание класса (наследуем его от CObject) - члены и методы класса.
- Включить в заголовочный файл документа ссылку на новый файл.
- Создать объект класса.
- Использовать новый объект (его методы) в приложении.
- Включить в определение класса макрос Visual C++ DECLARE_SERIAL, объявляющий методы, используемые в процессе сериализации.
- Переопределить метод Serialize() класса CObject.
- Для написания новой версии Serialize(), добавить в проект новый файл .cpp и определить в нем метод Serialize().
Реализуем пользовательский класс CData, наследуемый от CObject. Для этого создадим заголовочный файл Data.h и включим его в проект.
class CData: public CObject { private: CString data; DECLARE_SERIAL(CData); public: CData(){data=CString("");} void AddText(CString text){data+=text;} void DrawText(CDC* pDC) {pDC->TextOut(0, 0, data);} void ClearText(){data="";} void Serialize(CArchive& archive); };
Подключим Data.h в файл SerializerDoc.h, для этого добавим в начало файла строку:
#include "Data.h"
В классе документа введем переменную типа CData
… public: CData DataObject; …
Добавим в документ вида метод OnChar(…) и используем методы объекта DataObject.
void CSerializerView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default CSerializerDoc* pDoc=GetDocument(); ASSERT_VALID(pDoc); if(!pDoc) return; pDoc->DataObject.AddText(CString(char(nChar))); Invalidate(); CView::OnChar(nChar, nRepCnt, nFlags); }
Метод OnDraw:
void CSerializerView::OnDraw(CDC* pDC) { CSerializerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->DataObject.DrawText(pDC); }
Добавим в проект новый файл Data.cpp и определим Serialize(CArchive &archive)
#include "stdafx.h" #include "SerializerDoc.h" void CData::Serialize(CArchive& archive) { CObject::Serialize(archive); // Вызов метода Serialize() базового класса (CObject) if(archive.IsStoring())archive<<data; else archive>>data; } IMPLEMENT_SERIAL(CData, CObject, 0)
Макрос IMPLEMENT_SERIAL содержит дополнительные методы, ипользуемые Visual C++ для сериализации. Чтобы выполнить сериализацию для объекта DataObject класса CData, следует вызвать его метод Serialize(…) внутри метода Serialize(…) документа.
void CSerializerDoc::Serialize(CArchive& ar) { DataObject.Serialize(ar); // if (ar.IsStoring()) //{ // TODO: add storing code here //} //else //{ // TODO: add loading code here //} }
Работа с файлами. Класс CFile
Некоторые методы класса CFile:
Метод | Назначение |
---|---|
Abort | Закрывает файл, игнорируя любые предупреждения и ошибки |
Close | Закрывает файл и удаляет объект |
GetLength | Получает длину файла |
GetPosition | Получает текущую позицию файлового указателя |
Open | Производит открытие файла с возможностью проверки ошибок |
Read | Читает данные из файла с текущей позиции |
Remove | Удаляет заданный файл |
Rename | Переименовывает заданный файл |
Seek | Перемещает файловый указатель в заданную позицию |
SeekToBegin | Перемещает файловый указатель в начало файла |
SeekToEnd | Перемещает файловый указатель в конец файла |
SetLength | Изменяет длину файла |
Write | Записывает данные в файл с текущей позиции |
Режимы открытия файлов в конструкторе класса CFile:
Константа | Назначение |
---|---|
CFile::modeCreate | создает новый файл |
CFile::modeNoTruncate | Комбинируеся с modeCreate - если создаваемый файл уже существует, он не обрезается до нулевой длины |
CFile::modeRead | Открывает файл только для чтения |
CFile::modeReadWrite | Открывает файл для чтения - записи |
CFile::modeWrite | Открывает файл только для записи |
CFile::typeBinary | Устанавливает двоичный режим |
CFile::typeText | Устанавливает текстовый режим со специальной обработкой пар символов конца/перевода строки |
Задача: Написать программу, которая на базе диалогового окна по нажатию кнопки записывает в заданный файл на диске некоторый текст и считывает этот текст в тектовое поле в обратном порядке. Создадим на базе диалогового окна программу Filer. Создадим 4 текстовых записи, длина каждой записи 20 символов. Реализуем эти записи в программе как массив из 4-х символьных строк OutString.
… protected: HICON m_hIcon; char OutString[4][20]; …
Добавим в диалоговое окно необходимые управляющие элементы - два текстовых поля и кнопку. Для кнопки создадим функцию обработчик, создадим также две переменные m_text1 и m_text2 и свяжем их с текстовыми полями. Имя кнопки: Write and Read.
BOOL CFilerDlg::OnInitDialog() { CDialog::OnInitDialog(); strcpy(OutString[0], "Иллюстрация "); strcpy(OutString[1], "работы "); strcpy(OutString[2], "с "); strcpy(OutString[3], "файлами "); m_text1 = CString(OutString[0]) + CString(OutString[1]) + CString(OutString[2]) + CString(OutString[3]); UpdateData(false); }
Для записи данных в файл будем использовать класс CFile. При нажатии на кнопку будет создаваться и открываться для записи файл data.txt. Запись будем производить с помощью метода Write(…), для чтения используем методы Read(…) и Seek(…) класса CFile.
void CFilerDlg::OnButton1() { UpdateData(true); CFile OutFile("data.txt",CFile::modeCreate | CFile::modeWrite); //Создание обекта OutFile.Write(m_text1.GetBuffer(), m_text1.GetLength()); //Запись данных в файл OutFile.Close(); //Закрытие файла CFile InFile("data.txt",CFile::modeRead); ULONGLONG pos = InFile.SeekToEnd(); while(pos != CFile::begin) { char buf; InFile.Seek(--pos,CFile::begin); int n = InFile.Read(&buf,1); m_text2+=CString(buf); } UpdateData(false); InFile.Close(); }