Skip to content Skip to footer

Перевод книги Julian’а Smart’а – Глава XIV – Файлы и потоки

Скачать PDF-версию (262 кб)

В этой главе рассказывается о классах, которыми располагает библиотека wxWidgets для низкоуровневого доступа к файлам и потокам. Классы потоков wxWidgets не только защищают ваше приложение от особенностей различных стандартных библиотек C++, но также предоставляют множество полезных функций, включая сжатие, запись в zip-архивы и даже потоковые сокеты. Также рассматривается механизм виртуальных файловых систем, который позволяет вашему приложению легко получать данные из источника, отличного от обычных дисковых файлов.

Классы и функции для работы с файлами

wxWidgets содержит множество различных функций и классов для платформо-независимой работы с файлами. Мы рассмотрим только часто используемых представителей таких классов.

wxFile и wxFFile

wxFile может быть использован для низкоуровневого ввода/вывода. Он содержит все обычные функции для работы с целочисленными файловыми дескрипторами (открытие/закрытие, чтение/запись, позицирование и т.п.), но в отличие от стандартных функций языка C данный класс сообщает об ошибках через wxLog и автоматически закрывает файл в деструкторе. wxFFile предоставляет похожую функциональность, но использует буферизованный ввод/вывод и содержит указатель на дескриптор типа FILE.
Вы можете создать объект класса wxFile, используя конструктор по умолчанию, а далее вызвать метод Create или Open. Вы также можете использовать конструктор, который в качестве параметров принимает имя открываемого файла и режим работы:

  • wxFile::read — для чтения,
  • wxFile::write — для записи,
  • wxFile::read_write — для чтения и записи.

Вы также можете создать wxFile из существующего файлового дескриптора, который можно передать в конструктор или с помощью метода Attach. wxFile можно принудительно закрыть вызвав метод Close, который также вызывается автоматически при уничтожении объекта.

Для чтения данных из файла существует метод Read, которому передается указатель на буфер void* и число байт для чтения. Функция возвращает число считанных байтов или wxInvalidOffset, в случае ошибки. Используйте метод Write, чтобы записать буфер void* или строку типа wxString в файл. Метод Flush вызывает немедленную запись измененных данных в файл.

Для проверки достижение конца файла используйте метод Eof, который возвращает true, если указатель находится к конце файла (это отличается от поведения функции Eof класса wxFFile, которая возвращает true только после попытки прочитать информацию, расположенную за последним байтом). Размер файла можно определить вызвав Length.

Seek и SeekEnd устанавливают текущую позицию в файле, отсчитывая смещение от начала или конца файла соответственно. Tell возвращает текущее смещение от начала файла с типом wxFileOffset (64-битовое целое число в случае поддержки платформой или 32-битовое целое число в противном случае).

Вызвав статическую функцию Access вы можете определить возможно ли открыть файл в выбранном режиме. Другая статическая функция Exists проверяет существование указанного файла.

Следующий фрагмент кода использует wxFile, чтобы открыть файл с данными и прочитать их в массив.

#include "wx/file.h"

if (!wxFile::Exists(wxT("data.dat")))
    return false;

wxFile file(wxT("data.dat"));

if ( !file.IsOpened() )
    return false;

// берем размер файла
wxFileOffset nSize = file.Length();
if ( nSize == wxInvalidOffset )
    return false;

// читаем весь файл в память
wxUint* data = new wxUint8[nSize];

if ( fileMsg.Read(data, (size_t) nSize) != nSize )
{
    delete[] data;
    return false;
}

file.Close();

Для иллюстрации записи в файл приведем пример функции, которая сохраняет содержимое текстового элемента управления в файл.

bool WriteTextCtrlContents(wxTextCtrl* textCtrl,
                           const wxString& filename)
{
    wxFile file;
    if (!file.Open(filename, wxFile::write))
        return false

    int nLines = textCtrl->GetNumberOfLines();
    bool ok = true;

    for ( int nLine = 0; ok && nLine < nLines; nLine++ )
    {
        ok = file.Write(textCtrl->GetLineText(nLine) +
                        wxTextFile::GetEOL());
    }

    file.Close();
    return ok;
}
wxTextFile

wxTextFile реализует довольно прямолинейный путь для чтения и записи маленьких текстовых файлов и их построчной обработки.

Используйте метод Open, чтобы прочитать файл в память и разбить его на строки. Метод Write позволяет сохранить информацию в файл. GetLine или прямое обращение по индексу в массиве позволяет получить требуемую строку. Также перебор строк можно осуществить, используя GetFirstLine, GetNextLine и GetPrevLine. Функции AddLine и InsertLine позволяют добавить строку в конец файла, а удалить требуемую строку можно с помощью RemoveLine. Для полной очистки содержимого файла используйте метод Clear.
В следующем примере к каждой строке в файле добавляется заданная строка, после чего файл записывается обратно на диск.

#include "wx/textfile.h"

void FilePrepend(const wxString& filename, const wxString& text)
{
    wxTextFile file;
    if (file.Open(filename))
    {
        size_t i;
        for (i = 0; i < file.GetLineCount(); i++)
        {
            file&#91;i&#93; = text + file&#91;i&#93;;
        }
        file.Write(filename);
    }
}
&#91;/sourcecode&#93;
<h5>wxTempFile</h5>
<code>wxTempFile</code> реализует относительно безопасный метод для замены содержимого существующего файла. Класс <code>wxTempFile</code> является потомком от <code>wxFile</code> и использует временные файлы для записи данных. При этом реальная перезапись выбранного файла происходит только после вызова метода <code>Commit</code>. Использование временных файлов для записи пользовательских данных является отличной идеей, так как в этом случае минимизируются последствия возможных ошибок, которые могут произойти во время записи (таких как выключение электричества, ошибки в программе или других катаклизмов). При использовании временных файлов такие неприятности не смогут повредить текущему файлу на диске.

Замечание: Текущая реализация архитектуры документ/вид не использует временные файлы при создании выходного потока и вызове <code>SaveObject</code>, поэтому вы возможно захотите перегрузить метод <code>DoSaveDocument</code> и создать поток <code>wxFileOutputStream</code> из объекта типа <code>wxTempFile</code>. После записи необходимых данных вызовите метод <code>Sync</code> потокового объекта, а далее <code>Commit</code> для временного файла.
<h5>wxDir</h5>
<code>wxDir</code> --- это переносимый эквивалент функций open/read/closedir из системы unix, которые поддерживают перечисление файлов в каталоге. <code>wxDir</code> поддерживает перечисление как файлов, так и каталогов. Данный класс предоставляет гибкий механизм для рекурсивного перечисления файлов, а также более простую в использовании функцию <code>GetAllFiles</code>.

После открытия каталога с помощью <code>Open</code> (или передав нужный путь в конструктор) вызовите <code>GetFirst</code>, передав ему в качестве параметра указатель на строку в которую будет записано найденное имя файла. Дополнительно можно передать фильтр для файлов (по умолчанию пустая строка означает отсутствие фильтра) и необязательные флаги. Далее вызывайте <code>GetNext</code> до тех пор пока не переберете все файлы и функция не возвратит <code>false</code>. Фильтр может содержать групповые символы, такие как "*" (означает любое число любых символов) и "?" (означает один любой символ). Поле с флагами может быть комбинацией одной или нескольких битовых констант <code>wxDIR_FILES</code> (искать файлы), <code>wxDIR_DIRS</code> (искать каталоги), <code>wxDIR_HIDDEN</code> (искать скрытые файлы) и <code>wxDIR_DOTDOT</code> (добавлять к результату псевдокаталоги "." и ".."). По умолчанию ищутся все возможные объекты, кроме "." и "..".

Пример:
[sourcecode language="cpp"]
#include "wx/dir.h"

wxDir dir(wxGetCwd());

if ( !dir.IsOpened() )
{
    // Здесь разбираемся с возникшей ошибкой. wxDir уже показал пользователю
    // сообщение, объясняющее точную причину ошибки
    return;
}

puts("Enumerating object files in current directory:");

wxString filename;
wxString filespec = wxT("*.*");
int flags = wxDIR_FILES|wxDIR_DIRS;
bool cont = dir.GetFirst(&filename, filespec, flags);
while ( cont )
{
    wxLogMessage(wxT("%s\n"), filename.c_str());

    cont = dir.GetNext(&filename);
}

Заметим, что при возникновении проблем при перечислении файлов (например, в случае недоступности каталога) wxDir покажет пользователю сообщение об ошибке. Используйте класс wxLogNull, чтобы временно отключить данное поведение:

// Создаем область действия для logNull
{
    wxLogNull logNull;

    wxDir dir(badDir);
    if ( !dir.IsOpened() )
    {
        return;
    }
}
wxFileName

Класс wxFileName манипулирует именем файла. Он может разбирать и восстанавливать компоненты полного имени файла, а также реализует различные файловые операции, которые доступны в виде статических функций. Далее приведено несколько примеров того, что можно сделать с помощью данного класса. Ознакомьтесь с документацией, чтобы узнать об остальных поддерживаемых операциях.

#include "wx/filename.h"

// Создать имя файла из строки
wxFileName fname(wxT("MyFile.txt"));

// Нормализовать имя, что в том числе означает, что имя
// будет являться полным в системе Windows
fname.Normalize(wxPATH_NORM_LONG|wxPATH_NORM_DOTS|wxPATH_NORM_TILDE|
                wxPATH_NORM_ABSOLUTE);

// Получить полный путь до файла в виде строки
wxString filename = fname.GetFullPath();

// Получить имя файла относительно текущего каталога
fname.MakeRelativeTo(wxFileName::GetCwd());

// Существует ли заданный файл?
bool exists = fname.FileExists();

// Существует ли другой файл?
bool exists2 = wxFileName::FileExists(wxT("c:\\temp.txt"));

// Возвращаем имя файла
wxString name = fname.GetName();

// Возвращаем путь до файла
wxString path = fname.GetPath();

// Возвращаем короткое имя файла в Windows или
// идентичное ему в других ОС
wxString shortForm = fname.GetShortPath();

// Создаем каталог
bool ok = wxFileName::Mkdir(wxT("c:\\thing"));
Функции работы с файлами

Большинство часто используемых функций приведено в таблице~\ref{tab_filefunc} и определены в файле wx/filefn.h. Также ознакомьтесь также с классом wxFileName, особенно с его статическими функциями (например, wxFileName::FileExists), которые можно использовать без создания объекта класса wxFileName.

В wxWidgets существуют аналоги большинства стандартных функций языка C, такие как wxFopen, wxFputc и wxSscanf. Эти функции в данный момент не документированы, но их определение можно посмотреть в файле include/wx/wxchar.h дистрибутива wxWidgets.

Также полезно знать о символе wxFILE_SEP_PATH, который соответствует разделителю пути для текущей платформы (например, обратный слеш для систем Windows и прямой слеш для систем на базе Unix).

Классы потоков

Потоки — это высокоуровневая модель для чтения и записи данных. Вы можете писать свой код не заботясь о том куда осуществлять запись: в файлы, в память или даже в сокеты (за примером использования потоков с сокетами обратитесь к Главе 18 Программирование с использованием wxSocket). Некоторые классы wxWidgets (например, wxImage), поддерживающие чтение/запись файлов, также могут использовать ввод/вывод в поток.

wxStreamBase является базовым классом для всех потоков и объявляет функции такие как OnSysRead и OnSysWrite, которые должны быть реализованы в дочерних классах. Его дочерние классы wxInputStream и wxOutputStream образуют фундамент для всех других классов, осуществляющих чтение и запись, например для wxFileInputStream и wxFileOutputStream соответственно. Рассмотрим потоковые классы, предоставляемые библиотекой wxWidgets.

Файловые потоки

Классы wxFileInputStream и wxFileOutputStream базируются на wxFile и могут быть инициализированы именем файла, объектом wxFile или целочисленным файловым дескриптором. Далее приводится пример использования wxFileInputStream для чтения данных, перехода в начало файла и взятия текущего положения в файле.

#include "wx/wfstream.h"

// Конструктор инициализирует буфер потока и открывает файловый
// дескриптор, ассоциированный с именем файла.
// wxFileInputStream закроет файловый дескриптор при вызове деструктора
wxFileInputStream inStream(filename);

// Читаем несколько байтов
int byteCount = 100;
char data[100];

if (inStream.Read((void*) data, byteCount).
             LastError() != wxSTREAM_NOERROR)
{
    // Возникли какие-то проблемы.
    // За полным списком проблем обратитесь к документации по wxStreamBase
}

// Вы также можете получить число байт, которое реально
// было считано из потока
size_t reallyRead = inStream.LastRead();

// Перемещаемся в начало потока. SeekI возвратит позицию
// в потоке, считая от начала
off_t oldPosition = inStream.SeekI(0, wxFromBeginning);

// Получить текущую позицию в файле
off_t position = inStream.TellI();

Использование wxFileOutputStream аналогично. Следующий код использует wxFileInputStream и wxFileOutputStream для копирования файла (в целях эффективности файл копируется блоками по 1024 байта). Для большей ясности все проверки на возможные ошибки удалены.

// Копируем данные фиксированного размера из входного потока в
// выходной, используя промежуточный буфер для повышения эффективности
void BufferedCopy(wxInputStream& inStream, wxOutputStream& outStream,
                  size_t size)
{
    static unsigned char buf[1024];
    size_t bytesLeft = size;

    while (bytesLeft > 0)
    {
        size_t bytesToRead = wxMin((size_t) sizeof(buf), bytesLeft);

        inStream.Read((void*) buf, bytesToRead);
        outStream.Write((void*) buf, bytesToRead);

        bytesLeft -= bytesToRead;
    }
}

void CopyFile(const wxString& from, const wxString& to)
{
    wxFileInputStream inStream(from);
    wxFileOutputStream outStream(to);

    BufferedCopy(inStream, outStream, inStream.GetSize());
}

Классы wxFFileInputStream и wxFFileOutputStream идентичны wxFileInputStream и wxFileOutputStream, за исключением того, что они базируются на классе wxFFile вместо wxFile. Поэтому их можно инициализировать указателем на FILE или объектом wxFFile. Обработка конца файла также отличается: wxFileInputStream возвращает wxSTREAM_EOF после чтения последнего байта, тогда как wxFFileInputStream возвращает wxSTREAM_EOF при попытке считать данные, расположенны за последним байтом.

Строковые потоки и потоки в памяти

wxMemoryInputStream и wxMemoryOutputStream используют внутренний буфер для организации потока. Конструкторы обоих этих классов берут в качестве параметров буфер char* и его размер. Данные параметры могут быть опущены, чтобы позволить классу выделять память автоматически по мере необходимости. Мы рассмотрим применение данных классов чуть позже.

wxStringInputStream берет в качестве параметра строку из которой будет осуществляться чтение. wxStringOutputStream получает необязательный указатель на строку класса wxString, в которую будет осуществляться запись. Если данный параметр опущен, то класс самостоятельно создаст строку, доступ к которой можно получить через метод GetString.

Чтение и запись типов данных

До этого времени мы рассматривали классы, которые работают с сырыми байтами, которые необходимо преобразовать в что-нибудь полезное для приложения. Чтобы это сделать вы можете использовать четыре класса, которые реализуют чтение/запись на более высоком уровне: wxTextInputStream, wxTextOutputStream, wxDataInputStream и wxDataOutputStream. При создании данных объектов требуется указать ссылку на существующий поток, а классы предоставят методы для вставки и извлечения данных для различных типов языка C++.

wxTextInputStream читает данные из обычного текстового файла. Если вы обрабатываете файл с помощью wxTextInputStream, то вы должны делать проверку на достижение конца файла перед чтением следующего элемента. Однако вы должны быть готовы получить пустой элемент (такой как пустую строку или нулевое число) в конце файла. Это происходит из-за того, что в большинстве файлов в конце находится несколько пробельных символов (обычно это символ перехода на новую строку). Далее вы видите пример использования wxTextInputStream вместе с wxFileInputStream:

wxFileInputStream input( wxT("mytext.txt") );
wxTextInputStream text( input );
wxUint8 i1;
float f2;
wxString line;
text >> i1;       // читаем 8-битовое целое число
text >> i1 >> f2; // читаем 8-битовое целое, а далее вещественное число
text >> line;     // читаем текстовую строку

wxTextOutputStream записывает текстовые данные в выходной поток. При записи используется родной для системы признак окончания строки. Следующий пример записывает данные в стандартный поток для вывода ошибок.

#include "wx/wfstream.h"
#include "wx/txtstrm.h"

wxFFileOutputStream output( stderr );
wxTextOutputStream cout( output );

cout << wxT("This is a text line") << endl;
cout << 1234;
cout << 1.23456;
&#91;/sourcecode&#93;
<code>wxDataInputStream</code> и <code>wxDataOutputStream</code> похожи, но работают с бинарными данными. Данные записываются в переносимом формате, поэтому файл будет иметь одинаковую структуру, назависимо от платформы. Далее приведен пример чтения из файла с данными:
[sourcecode language="cpp"]
#include "wx/wfstream.h"
#include "wx/datstrm.h"

wxFileInputStream input( wxT("mytext.dat") );
wxDataInputStream store( input );
wxUint8 i1;
float f2;
wxString line;

store >> i1;       // читаем 8-битовое целое
store >> i1 >> f2; // читаем 8-битовое целое, а далее вещественное число
store >> line;     // читаем текстовую строку

И записи данных в файл:

#include "wx/wfstream.h"
#include "wx/datstrm.h"

wxFileOutputStream output(wxT("mytext.dat") );
wxDataOutputStream store( output );

store << 2 << 8 << 1.2;
store << wxT("This is a text line");
&#91;/sourcecode&#93;
<h5>Потоки на базе сокетов</h5>
Классы <code>wxSocketOutputStream</code> и <code>wxSocketInputStream</code> инициализируются существующим объектом <code>wxSocket</code>. Обратитесь к Главе 18 за дополнительными деталями.
<h5>Потоки фильтрации</h5>
<code>wxFilterInputStream</code> и <code>wxFilterOutputStream</code> являются базовыми для потоков, которые могут быть помещены над остальными потоками. Примером фильтрующего потока является поток <code>wxZlibInputStream</code>. Если вы передаете ему входной поток, созданный из сжатого zlib или glib файла, то вы можете читать данные из потока <code>wxZlibInputStream</code> без необходимости заботится об их декомпрессии. Аналогично, вы можете создать <code>wxZlibOutputStream</code> и связать с ним выходной поток, такой как <code>wxFileOutputStream</code>. Запись данных в поток <code>wxZlibOutputStream</code> приведет к тому, что они будут сжаты и посланы в <code>wxFileOutputStream</code>.

Следующий пример сжимает строку с данными (buf) в поток в памяти, а далее копирует полученные данные в постоянное хранилище (data).

Следующий пример сжимает строку с данными (buf) в поток в памяти, а далее копирует полученные данные в постоянное хранилище (data).
[sourcecode language="cpp"]
#include "wx/mstream.h"
#include "wx/zstream.h"

const char* buf =
    "01234567890123456789012345678901234567890123456789";

// Создаем выходной zlib-поток, связанный с потоком в памяти
wxMemoryOutputStream memStreamOut;
wxZlibOutputStream zStreamOut(memStreamOut);

// Записываем содержимое 'buf' в память через zlib-поток
zStreamOut.Write(buf, strlen(buf));

// Получаем размер полуившегося буфера
int sz = memStreamOut.GetSize();

// Создаем хранилище достаточное для хранения сжатых данных и
// копируем туда данные из памяти
unsigned char* data = new unsigned char[sz];
memStreamOut.CopyTo(data, sz);
Потоки с сжатием

wxZipInputStream является достаточно сложным классом, позволяющим работать с архивами, а не только с линейным потоком данных. Обычно работу с архивами производят с помощью группы классов, включающих в себя wxArchiveClassFactory и wxArchiveEntry, но существует возможность читать и писать zip-файлы без прямого их использования. Для использования wxZipInputStream вы можете создать поток из wxInputStream (который самостоятельно откроет архив) или указав имя архива и имя файла внутри архива. Оба метода проиллюстрированы в следующем примере, в котором строки читаются из одного или нескольких файлов внутри архива (первый пример) или из одного явно определенного файла (второй пример).

#include "wx/wfstream.h"
#include "wx/zipstrm.h"
#include "wx/txtstrm.h"

// Метод 1: создание zip-потока для чтения в два шага

wxZipEntry* entry;

wxFFileInputStream in(wxT("test.zip"));
wxZipInputStream zip(in);
wxTextInputStream txt(zip);
wxString data;

while (entry = zip.GetNextEntry())
{
    wxString name = entry->GetName();    // доступ к метаданным
    txt >> data;                         // доступ к данным
    delete entry;
}

// Метод 2: определяем требуемый файл в конструкторе

wxZipInputStream in(wxT("test.zip"), wxT("text.txt"));
wxTextInputStream txt(zip);

wxString data;
txt >> data;                             // доступ к данным

Класс wxZipOutputStream служит для записи в zip-файлы. Методы PutNextEntry или PutNextDirEntry используются для создания новых файлов внутри архива в которые позже может быть осуществлена запись. Например:

#include "wx/wfstream.h"
#include "wx/zipstrm.h"
#include "wx/txtstrm.h"

wxFFileOutputStream out(wxT("test.zip"));
wxZipOutputStream zip(out);
wxTextOutputStream txt(zip);

zip.PutNextEntry(wxT("entry1.txt"));
txt << wxT("Some text for entry1\n");

zip.PutNextEntry(wxT("entry2.txt"));
txt << wxT("Some text for entry2\n");
&#91;/sourcecode&#93;
<h5>Виртуальные файловые системы</h5>
В wxWidgets реализован механизм виртуальных файловых систем, который позволяет читать данные из различных источников также как из обычных файлов. С помощью данного механизма можно читать из zip-архивов, из памяти и по протоколам HTTP и FTP. Хотя он не может быть использован для записи редактируемых документов (так как это инструмент для доступа только для чтения), но его можно, к примеру, использовать для доступа к ресурсам в zip-архиве. Класс <code>wxHtmlWindow</code> (механизм помощи wxWidgets на базе HTML) и ресурсы XRC умеют работать с виртуальными файловыми системами. Виртуальные файловые системы более удобны для работы с архивами, чем рассмотренные ранее классы, но последние позволяют не только читать, но и писать в архивы. Хотя оба механизма и используют потоки, но они никак не связаны друг с другом.

В библиотеке реализованы различные виртуальные файловые системы с помощью классов-наследников от <code>wxFileSystemHandler</code>. Экземпляры этих объектов должны быть добавлены через <code>wxFileSystem::AddHandler</code> (чаще всего это делают в функции инициализации приложения <code>OnInit</code>) перед использованием этих файловых систем. Обычно все взаимодействие с файловой системой осуществляется через <code>wxFileSystem</code>, но иногда обработчики предоставляют функции, которые могут использоваться приложением непосредственно, как например функции <code>AddFile</code> и <code>RemoveFile</code> класса <code>wxMemoryFSHandler</code>.

Перед тем как детально изучить взаимодействие приложения с API виртуальной файловой системой рассмотрим возможность использования ее косвенным образом через другие подсистемы библиотеки wxWidgets. Вот пример URL, который может быть использован в HTML-файле, отображаемом <code>wxHtmlWindow</code>:
[sourcecode language="xml"]
<img src="file:myapp.bin#zip:images/logo.png">

Часть до знака решетка (#) — это имя архива, а часть после решетки — это имя протокола файловой системы и местоположение файла внутри архива.

Похожим образом мы можем определить положение виртуального файла внутри XRC-файла:

<object class="wxBitmapButton">
    <bitmap>file:myapp.bin#zip:images/fuzzy.gif</bitmap>
</object>

В данном примере код, использующий виртуальные файловые системы, скрыт в реализации wxHtmlWindow и XRC. Чтобы напрямую использовать виртуальные файловые системы вы можете использовать классы wxFileSystem и wxFSFile. В следующем коде wxBitmap формируется из изображения, находящегося в zip-архиве. При инициализации приложения с помощью wxFileSystem регистрируется wxZipFSHandler. Приложение использует экземпляр класса wxFileSystem (который можно создать временно или использовать на протяжении всей жизни программы), чтобы открыть файл logo.png из архива myapp.bin. Возвращаемый объект класса wxFSFile опрашивается на предмет ассоциированного с ним потока, который в дальнейшем используется для чтения изображения (wxImage умеет инициализироваться из потока). Далее изображение преобразовывается в wxBitmap, а объекты wxFSFile и wxFileSystem удаляются.

#include "wx/fs_zip.h"
#include "wx/filesys.h"
#include "wx/wfstream.h"

// Это нужно сделать один раз при инициализации приложения
wxFileSystem::AddHandler(new wxZipFSHandler);

wxFileSystem* fileSystem = new wxFileSystem;

wxString archive = wxT("file:///c:/myapp/myapp.bin");
wxString filename = wxT("images/logo.png");

wxFSFile* file = fileSystem->OpenFile(
              archive + wxString(wxT("#zip:")) + filename);
if (file)
{
    wxInputStream* stream = file->GetStream();

    wxImage image(* stream, bitmapType);
    wxBitmap bitmap = wxBitmap(image);

    delete file;
}
delete fileSystem;

Заметим, что в метод wxFileSystem::OpenFile нужно передавать URL, а не обычное имя файла. Когда вы определяете абсолютное положение файла в правой части URL необходимо использовать форму file:/<имя_хоста>//<файл>. Если имя хоста необходимо опустить, то нужно поставить три слеша. Вы можете преобразовать имя файла в URL используя метод wxFileSystem::FileNameToURL и сделать обратное преобразование с помощью wxFileSystem::URLToFileName.

Продемонстрируем использование zip-архива на примере функции LoadTextResource, которая загружает текстовый файл в кодировке ASCII (такой как HTML файл) в переменную text из заданного архива archive и файла filename внутри его.

// Загружаем текстовый ресурс из архива
bool LoadTextResource(wxString& text, const wxString& archive,
                      const wxString& filename)
{
    wxString archiveURL(wxFileSystem::FileNameToURL(archive));
    wxFileSystem* fileSystem = new wxFileSystem;

    wxFSFile* file = fileSystem->OpenFile(
                 archiveURL + wxString(wxT("#zip:")) + filename);
    if (file)
    {
        wxInputStream* stream = file->GetStream();
        size_t sz = stream->GetSize();
        char* buf = new char[sz + 1];
        stream->Read((void*) buf, sz);
        buf[sz] = 0;

        text = wxString::FromAscii(buf);

        delete[] buf;

        delete file;
        delete fileSystem;
        return true;
    }
    else
        return false;
}

Класс wxMemoryFSHandler позволяет хранить данные в памяти приложения и обращаться к ним как к файлам. Очевидно, что таким образом не стоит хранить в памяти огромные объемы данных, но иногда полезно иметь такую возможность, вместо того, чтобы работать с файлами непосредственно на диске. Например, во время предпросмотра XRC-диалогов DialogBlocks добавляет ссылку на рисунок в памяти, если пользовательский рисунок не доступен. Эти рисунки не существуют на диске, однако на них все равно существует ссылка в XRC, как если бы это были обычные файлы:

<object class="wxBitmapButton">
    <bitmap>memory:default.png</bitmap>
</object>

wxMemoryFSHandler имеет перегруженную функцию AddFile, которая получает в качестве аргумента имя файла и ссылку на wxImage, wxBitmap, wxStringvoid*. Когда эти данные больше не нужны, вы можете удалить их с помощью RemoveFile. Например:

#include "wx/fs_mem.h"
#include "wx/filesys.h"
#include "wx/wfstream.h"
#include "csquery.xpm"

wxFileSystem::AddHandler(new wxMemoryFSHandler);

wxBitmap bitmap(csquery_xpm);
wxMemoryFSHandler::AddFile(wxT("csquery.xpm"), bitmap,
                           wxBITMAP_TYPE_XPM);
...
wxMemoryFSHandler::RemoveFile(wxT("csquery.xpm"));

Третий (и последний) обработчик для виртуальных файловых систем, поддерживаемых wxWidgets, называется wxInternetFSHandler, который используется для работы с протоколами FTP и HTTP.

Итоги

В этой главе дан обзор классов wxWidgets, которые позволяют вашему приложению переносимо работать с файлами и потоками. Мы также дали небольшой обзор механизма виртуальных файловых систем, который позволяет легко получать данные из сжатых архивов, из данных в памяти и из файлов в сети Интернет.

Далее мы рассмотрим темы, которые напрямую не относятся к тому, что видят на экране пользователи, но несмотря на это очень важные: про управление памятью, отладку и обработку ошибок.

1 Comment

  • Евгений aka SmileGobo
    Posted March 24, 2009 at 00:49

    Я даже не знаю как вас благодарить!!!

Leave a comment

0.0/5