Класс IFile

В этом разделе представлены классы, которые предназначены для работы с абстрактными файлами. Методы этих классов аналогичны соответствующим функциям из библиотеки stdio.

class IReadFile
{
public:
    virtual nat  read ( void * p, const nat size, const nat count ) = 0;
    virtual bool getc ( void * p ) = 0;
    virtual ~IReadFile() {}
};

Класс IReadFile предназначен для чтения данных. Метод read считывает информацию блоками размером size и количеством count в память с адресом p. Возращаемое значение - это реальное количество прочитанных блоков. Метод getc пытается прочитать один байт и возращает true в случае успеха.

class IWriteFile
{
public:
    virtual nat write ( const void * p, const nat size, const nat count ) = 0;
    virtual bool putc ( const void * p ) = 0;
    virtual void flush() = 0;
    virtual ~IWriteFile() {}
};

Класс IWriteFile предназначен для записи данных. Метод write записывает информацию блоками размером size и количеством count из памяти с адресом p. Возращаемое значение - это реальное количество записанных блоков. Метод putc пытается записать один байт и возращает true в случае успеха. Метод flush сбрасывает буфер, если это было предусмотрено.

class ISeekFile
{
public:
    virtual bool seek_set ( long offset ) = 0;
    virtual bool seek_end ( long offset ) = 0;
    virtual bool seek_cur ( long offset ) = 0;
    virtual void rewind() = 0;
    virtual long tell() = 0;
    virtual ~ISeekFile() {}
};

Класс ISeekFile осуществляет перемещение по файлу. Методы seek_set, seek_end и seek_cur пытаются сместиться соответственно относительно начала, конца или текущей позиции на offset байт и возращают true в случае успеха. Метод rewind осуществляет переход на начало файла. Метод tell возращает текущее положение ( 0 - это начало файла ) или -1 в случае ошибки.

class IReadSeekFile : public IReadFile, public virtual ISeekFile {};

class IWriteSeekFile : public IWriteFile, public virtual ISeekFile {};

class IFile : public IReadSeekFile, public IWriteSeekFile {};

Не все файлы могут поддерживать навигацию по файлу. Те, которые это делают, должны быть производными от классов IReadSeekFile, IWriteSeekFile или IFile. Первый работает только на чтение, второй - только на запись, третий ( самый общий ) - и на чтение, и на запись. Обратите внимание, что здесь есть и множественное наследование, и виртуальные классы.

Теперь рассмотрим конкретные реализации этих абстрактных классов.

Класс RealFile, производный от класса IFile, является "настоящим" файлом в том смысле, что он ведёт себя точно также, как и файл из библиотеки stdio, и все его методы вызывают соответствующие функции из этой библиотеки. Вначале ( версия 2 ) я полагал, что эти функции корректно обрабатывают ситуацию, когда указатель file равен нулю, но позже я выяснил, что это не так. Поэтому в версии 2.1 во всех методах есть проверка указателя на ноль. Кроме того в классе появился метод isValid для проверки работоспособности файла.

class RealFile : public IFile
{
    struct _iobuf * file;
// Запрет конструктора копии и оператора присваивания:
    RealFile ( RealFile & );
    void operator = ( RealFile & );
public:
    RealFile(const char * filename, const char * mode);
    bool isValid() const { return file != 0; }
    unsigned int read (void * p, const unsigned int size, const unsigned int count);
    unsigned int write(const void * p, const unsigned int size, const unsigned int count);
    bool getc(void * p);
    bool putc(const void * p);
    bool seek_set(long offset);
    bool seek_end(long offset);
    bool seek_cur(long offset);
    long tell();
    void rewind();
    void flush();
    ~RealFile();
};

Класс PseudoReadFile производный от класса IReadFile называется псевдофайлом, потому-что он читает данные не из файла, а из памяти, заданной указателем на массив p:

class PseudoReadFile : public IReadFile
{
    const bit8 * pos;
public:
    explicit PseudoReadFile ( const bit8 * p ) : pos(p) {}
    nat read ( void * p, const nat size, const nat count );
    bool getc ( void * p );
};

Класс PseudoReadSeekFile производный от класса IReadSeekFile помимо чтения имеет навигацию:

class PseudoReadSeekFile : public IReadSeekFile
{
    CArrRef<bit8> buf;
    nat pos;
public:
    explicit PseudoReadSeekFile ( CArrRef<bit8> p ) : buf(p), pos(0) {}
    nat read(void * p, const nat size, const nat count);
    bool getc(void * p);
    bool seek_set(long offset);
    bool seek_end(long offset);
    bool seek_cur(long offset);
    long tell() { return pos; }
    void rewind () { pos = 0; }
};

Класс PseudoWriteFile производный от класса IWriteFile осуществляет запись в контейнер Suite<bit8> и лишён навигации.

class PseudoWriteFile : public IWriteFile
{
    Suite<bit8> & suite;
// Запрет конструктора копии и оператора присваивания:
    PseudoWriteFile ( PseudoWriteFile & );
    void operator = ( PseudoWriteFile & );
public:
    explicit PseudoWriteFile ( Suite<bit8> & p ) : suite(p) {}
    nat write(const void * p, const nat size, const nat count);
    bool putc(const void * p);
    void flush() {}
    long tell() { return pos; }
};

Класс PseudoFile может использоваться вместо трёх предыдущих.

class PseudoFile : public IFile
{
    DynArray<bit8> & buf;
    nat length, pos;
// Запрет конструктора копии и оператора присваивания:
    PseudoFile ( PseudoFile & );
    void operator = ( PseudoFile & );
public:
    PseudoFile ( nat n, DynArray<bit8> & p );
    nat read(void * p, const nat size, const nat count);
    bool getc(void * p);
    nat write(const void * p, const nat size, const nat count);
    bool putc(const void * p);
    void flush() {}
    bool seek_set(long offset);
    bool seek_end(long offset);
    bool seek_cur(long offset);
    void rewind() { pos = 0; }
    long tell() { return pos; }
};

Класс StringWriteFile предназначен для записи данных в строку:

class StringWriteFile : public IWriteFile
{
    Suite<char> suite;
// Запрет конструктора копии и оператора присваивания:
    StringWriteFile ( StringWriteFile & );
    void operator = ( StringWriteFile & );
public:
    StringWriteFile () { suite.inc() = 0; }

    nat write ( const void * p, const nat size, const nat count )
    {
        const nat n = size * count;
        const nat k = suite.size() - 1;
        suite.inc ( n );
        const char * s = (const char *) p;
        for ( nat i = 0; i < n; ++i ) suite[i+k] = s[i];
        suite.las() = 0;
        return count;
    }

    bool putc ( const void * p )
    {
        suite.las() = *(const char *) p;
        suite.inc() = 0;
        return true;
    }

    void flush() {}

    const char * operator () () const
    {
        return suite();
    }

    nat size () const
    {
        return suite.size() - 1;
    }

    void clear()
    {
        suite.resize(1).las() = 0;
    }
};

Если нужно прочитать из файла десятичное число заданное в виде текста, то можно воспользоваться следующими функциями:

bool readIntDec ( IReadFile & file, char & c, int32 & i );

inline bool readIntDec ( IReadFile & file, int32 & i )
{
    char c;
    return readIntDec ( file, c, i );
}

bool readIntDec ( IReadFile & file, char & c, nat32 & i );

inline bool readIntDec ( IReadFile & file, nat32 & i )
{
    char c;
    return readIntDec ( file, c, i );
}

bool readFltDec ( IReadFile & file, char & c, real32  & f );

inline bool readFltDec ( IReadFile & file, real32 & f )
{
    char c;
    return readFltDec ( file, c, f );
}

bool readFltDec ( IReadFile & file, char & c, real64 & d );

inline bool readFltDec ( IReadFile & file, real64 & d )
{
    char c;
    return readFltDec ( file, c, d );
}

с - это последний прочитанный символ.

Функция printf записывает данные в файл при помощи заданного формата. Он аналогичен стандартному, но пока ещё не все его свойства реализованы.

bool printf ( IWriteFile & file, const char * format, ... );

Описание шаблонов CArrRef, Suite и DynArray находится здесь.
Описание типов bit8, int32 и nat32 находится здесь.

Исходники находятся в source.zip.

Наверх