Шаг 177.
Библиотека Qt.
Загрузка и сохранение данных в приложении

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

    Для этого будем использовать объекты QFile и QDataStream, которые совместно обеспечивают независимый от платформы ввод-вывод в двоичном формате.

    Начнем с записи файла данных электноррой таблицы. Функция writeFile() вызывается из MainWindow::saveFile() для записи файла на диск. Она возвращает true при успешном завершении и false при ошибке.

bool Spreadsheet::writeFile(const QString &fileName)
{
    //создаем объект QFile, задавая имя файла
    QFile file(fileName);
    //вызываем функцию ореn() для открытия файла для записи данных
    if (!file.open(QIODevice::WriteOnly)) {
        QMessageBox::warning(this, tr("Электронная таблица"),
                             tr("Не удается записать файл %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }
    /*создаем объект QDataStream, который предназначен для работы
    с QFile и использует его для записи данных*/
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_5_2);
    out << quint32(MagicNumber);
    /*непосредственно перед записью данных изменяем курсор приложения
    на стандартный курсор ожидания (обычно он имеет вид песочных часов)*/
    QApplication::setOverrideCursor(Qt::WaitCursor);
    for (int row = 0; row < RowCount; ++row) {
        for (int column = 0; column < ColumnCount; ++column) {
            QString str = formula(row, column);
            if (!str.isEmpty())
    /*Поскольку элементарные целочисленные типы C++ на различных
    платформах могут иметь различный размер, надежнее преобразовать их типы в qint8,
    quint8, qint16, quint16, qint32, quint32, qint64 и quint64, что гарантирует
    использование объявленного в них размера (в битах)*/
                out << quint16(row) << quint16(column) << str;
        }
    }
    /*восстанавливаем нормальный курсор после окончания записи данных.
    В конце функции файл автоматически закрывается деструктором QFile*/
    QApplication::restoreOverrideCursor();
    return true;
}

    QDataStream поддерживает основные типы C++ совместно со многими типами Qt. Их синтаксис напоминает синтаксис классов <iostream> стандартного C++. Например, out << х << у << z; выполняет запись в поток значений переменных х, у и z, a in >> х >> у >> z; считывает их из потока.

    Файл данных приложения Электронная таблица имеет очень следующий формат. Он начинается с 32-битового числа, идентифицирующего формат файла ("волшебное" число MagicNumber определено в spreadsheet.h как 0x7F51C883 - произвольное случайное число). Затем идет последовательность блоков, содержащих строку, столбец и формулу одной ячейки. Для экономии места мы не записываем пустые ячейки. Формат показан на рис. 1.


Рис.1. Формат файла данных для приложения Электронная таблица

    Точно представление типов данных определяется в QDataStream. Например, quint16 представляется двумя байтами со старшим байтом в конце, a QString задается длиной строки, за которой следуют символы в коде Unicode.

    Двоичное представление типов в Qt достаточно сильно усовершенствовалось со времени выхода версии Qt 1.0. Такая тенденция, вероятно, сохранится в будущих версиях Qt, чтобы идти вровень с развитием существующих типов и обеспечить новые типы в Qt.

    По умолчанию класс QDataStream использует 15 версию двоичного формата в Qt 5.2, но он также может быть настроен на чтение прошлых версий. Для того чтобы избежать проблем совместимости при перекомпиляции приложения в будущем, в новой версии Qt мы заставляем QDataStream использовать версию 15 вне зависимости от версии Qt, в которой оно компилируется. (Для удобства используется константа QDataStream::Qt_5_2, равная 15).

    Класс QDataStream достаточно универсален. Он может использоваться для объекта QFile, но также и для QBuffer, QProcess, QTcpSocket, QUdpSocket или QSslSocket.

    Qt также предоставляет класс QTextStream, который может использоваться с QDataStream для чтения и записи текстовых файлов.

    Рассмотрим чтение данных из файла приложения Электронная таблица:

bool Spreadsheet::readFile(const QString &fileName)
{
    //для чтения файла мы пользуемся объектом QFile
    QFile file(fileName);
    //используем флажок QIODevice::Readonly
    if (!file.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, tr("Электронная таблица"),
                             tr("Не удается прочитать файл %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }

    QDataStream in(&file);
    /*устанавливаем версию QDataStream на значение 15.
    Формат чтения всегда должен совпадать с форматом записи*/
    in.setVersion(QDataStream::Qt_5_2);

    quint32 magic;
    in >> magic;
    if (magic != MagicNumber) {
        QMessageBox::warning(this, tr("Электронная таблица"),
            tr("Данный файл не является файлом электронной таблицы."));
        return false;
    }
    /*если в начале файла содержится правильное "волшебное"
    число, мы вызываем функцию сlear() для очистки в электронной таблице всех
    ячеек и затем считываем данные ячеек. Поскольку файл содержит только данные
    для непустых ячеек, маловероятно, что будет заполнена каждая ячейка электронной
    таблицы, поэтому мы должны очистить все ячейки перед чтением файла*/
    clear();

    quint16 row;
    quint16 column;
    QString str;

    QApplication::setOverrideCursor(Qt::WaitCursor);
    while (!in.atEnd()) {
        in >> row >> column >> str;
        setFormula(row, column, str);
    }
    QApplication::restoreOverrideCursor();
    return true;
}

    На следующем шаге рассмотрим реализацию слотов, относящихся к меню Edit данного приложения.




Предыдущий шаг Содержание Следующий шаг