Практика работы в среде визуального программирования
Сериализация. Работа с файлами
Сериализация объектов класса 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();
}