Опубликован: 10.03.2009 | Доступ: свободный | Студентов: 2297 / 279 | Оценка: 4.31 / 4.07 | Длительность: 09:23:00
Лекция 4:

Практика работы в среде визуального программирования

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >

Сериализация. Работа с файлами

Сериализация объектов класса CString

Сериализация - процесс записи (чтения) объектовов и данных на диск (с диска). Рассмотрим сериализацию как встроенных классов Visual C++ (например, CString ), так и нестандартных. Если в программе отсутствует документ, на который можно возложить выполнение файловых операций (программы на базе диалоговых окон), то работают с классом MFC CFile. Рассмотрим программу Writer, которая будет записывать на диск введенную строку, а затем, по требованию пользователя, загружать ее из файла.

  1. Создатим SDI программу Writer.
  2. Создадим объект CString StringData и инициализируем ее.
  3. Создадим обаботчик 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() - очистка (стирание содержимого) строки .

Алгоритм организации сериализации нестандартных объектов

  1. Создать SDI программу Serializer.
  2. Добавить в проект заголовочный файл.
  3. Включить в этот файл описание класса (наследуем его от CObject) - члены и методы класса.
  4. Включить в заголовочный файл документа ссылку на новый файл.
  5. Создать объект класса.
  6. Использовать новый объект (его методы) в приложении.
  7. Включить в определение класса макрос Visual C++ DECLARE_SERIAL, объявляющий методы, используемые в процессе сериализации.
  8. Переопределить метод Serialize() класса CObject.
  9. Для написания новой версии 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();
}
< Лекция 3 || Лекция 4: 12345 || Лекция 5 >