вторник, 3 января 2012 г.

Запись и чтение объектов классов с динамическими данными-членами вбинарные файлы (сериализация C++)

Предпосылки

Итак, встала передо мной задача - написать редактор математических матриц. Язык реализации - C++, непременно оконное приложение. Главная проблема - неизвестен тип данных, содержащихся в матрице. Хорошо, если это int (целый тип) или какой-нибудь строковый тип (фиксированный char, string и т.п.), можно с чистой душой использовать текстовый тип файла. Но когда доходит дело до дробей (тип float или даже double), тут встает проблема с этой-самой переменной точностью и бесконечным конвертированием туда-сюда из строкового в дробный тип и обратно. Но более логично в данном маленьком приложении использовать бинарные файлы. Благодаря им мы не потеряем ни грамма точности из этих типов. Файлы записываются последовательно, как и обычные текстовые. Главная проблема - определить длину данных, чтобы не "откусить хвост" уже записанному объекту.

Реализация

Задача оказалась не столь тривиальна, как на первый взгляд. Само-собой, о записи указателей в файл не может идти речи, т.к. при развертывании объекта новое положение значений будет гарантированно иным (а области памяти под старыми указателями почти наверняка окажутся недоступными), поэтому ловим экзепшн (ошибка во время исполнения, runtime error) по доступу к памяти . После курения форумов, где описаны по большей части проблемы, а не их решения, я аппроксимировал решение задачи следующим образом - запись/чтение по элементарным элементам. Ниже приведу пример класса матриц (двумерные), которые будут уложены в динамический линейный список (шаблон list from STL). Я не буду приводить лишний код интерфейса, только поверьте мне, что будет список (std::list) объектов матриц класса, приведенного ниже, который мы будем записывать и считывать из файла.

Описание класса матрицы:

// Matrix.h
//--------------------

#define MATRIX_TYPE int // Тип элементов матрицы
#include

using namespace std;

struct Matrix
{
    int N,M;
    MATRIX_TYPE **val;

    Matrix(){val = 0; N = M = 0;}
    Matrix(int n, int m, MATRIX_TYPE values[])
    {
        N = n;
        M = m;
        val = new MATRIX_TYPE*[N];
        for (register int i=0; i<N; i++)
        {
            val[i] = new MATRIX_TYPE[M];
            for (register int j=0; j<M; j++)
                val[i][j] = values[j+i*M];
        }
    }
    ~Matrix()
    {
        for (int i=0; i<N; i++)
            delete [] val[i];
        delete [] val;
    }
    };

list MatrixList; // Тут у нас матрицы

Действия по записи в файл

// Обработчик некой кнопки "Сохранить" (Savebtn)
void CMatrixMFCDlg::OnBnClickedSavebtn()
{
    ofstream file("Data.dat", ios::binary | ios::trunc);
    if (!file)
    {
        MessageBox("Error saving file Data.dat");
        return;
    }
    // В начале - записываем данные о кол-ве идущих на запись матрицах
    int Count = MatrixList.size();
    file.write((char*)&Count,sizeof(int));
    // Затем, перемещаясь по списку описываем матрицы
    for (list::iterator it = MatrixList.begin(); it != MatrixList.end(); it++)
    {
    // Данные записываются "в строчку" поэтому сначала делаем записи о кол-ве строк и столбцов
        file.write((char*)&(it->N), sizeof(int)); // Данные по строкам
        file.write((char*)&(it->M), sizeof(int)); // Данные по столбцам
        // Теперь записываем непосредственно каждый элемент матрицы
        for (int i=0; i N; i++)
        {
            for (int j=0; j M; j++)
            {
                // Сохранение элемента
                file.write((char*)&(it->val[i][j]),sizeof(MATRIX_TYPE));
            }

        }
    }
    file.close();
    }

Теперь будем читать записанное из файла:

// Обработчик кнопки "Загрузить" (Loadbtn)
void CMatrixMFCDlg::OnBnClickedLoadbtn()
{
    ifstream file("Data.dat", ios::binary);
    if (!file)
    {
        MessageBox("Error loading file Data.dat");
        return;
    }
    MatrixList.clear();
    int Count = 0;

    file.read((char*)&Count,sizeof(int));

    for (int k=0; k<Count; k++)
    {
        int n(0),m(0);
        file.read((char*)&n,sizeof(int));
        file.read((char*)&m,sizeof(int));
        // На основе кол-ва строк и столбцов создаем одномерный массив типа, аналогичного
// матричному и читаем в него значения
        MATRIX_TYPE* values = new MATRIX_TYPE[n*m];
        for (int i=0; i<n*m; i++)
            file.read((char*)&(values[i]),sizeof(MATRIX_TYPE));
            // Создаем и добавляем матрицу в список
        MatrixList.push_back(*(new Matrix(n,m,values)));
        delete [] values;
    }
    file.close();
    }

Комментариев нет:

Отправить комментарий