подпоток от istream - PullRequest
       6

подпоток от istream

4 голосов
/ 02 октября 2011

Предположим, у меня есть ifstream, представляющий большой файл, содержащий множество вложенных файлов. Я хочу иметь возможность создать «sub» istream из большего ifstream (с учетом размера и размера), представляющего часть файла, чтобы другой код мог читать из этого подпотока, как если бы он был независимым istream .

Есть идеи, как мне это сделать?

EDIT - Я бы предпочел избежать повышения.

Ответы [ 4 ]

5 голосов
/ 05 октября 2011

Это пример «фильтра» streambuf, который читает из содержащегося в нем streambuf, начиная с указанного места и читая до указанного размера. Вы создаете substreambuf, передавая исходные значения streambuf и substreambuf, а затем переводите доступ, чтобы все считывалось из нужного места в базовом streambuf.

Большинство накладных расходов, связанных с вызовами sgetc и snextc из underflow и uflow, должны оптимизироваться. Многие операторы извлечения работают побайтово, поэтому не должно быть дополнительных накладных расходов помимо поддержания позиции чтения в подразделе и проверки конца подраздела. Конечно, чтение больших кусков данных будет менее эффективным с этим классом (хотя это можно исправить).

Это все еще нуждается в улучшениях, таких как проверка того, что запрошенное местоположение находится внутри базового streambuf.

class substreambuf : public std::streambuf
{
public:

    substreambuf(std::streambuf *sbuf, std::size_t start, std::size_t len) : m_sbuf(sbuf), m_start(start), m_len(len), m_pos(0)
    {
        std::streampos p = m_sbuf->pubseekpos(start);
        assert(p != std::streampos(-1));
        setbuf(NULL, 0);
    }

protected:

    int underflow()
    {
        if (m_pos + std::streamsize(1) >= m_len)
            return traits_type::eof();
        return m_sbuf->sgetc();
    }

    int uflow()
    {
        if (m_pos + std::streamsize(1) > m_len)
            return traits_type::eof();
        m_pos += std::streamsize(1);
        return m_sbuf->sbumpc();
    }

    std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
    {
        std::streampos cursor;

        if (way == std::ios_base::beg)
            cursor = off;
        else if (way == std::ios_base::cur)
            cursor = m_pos + off;
        else if (way == std::ios_base::end)
            cursor = m_len - off;

        if (cursor < 0 || cursor >= m_len)
            return std::streampos(-1);
        m_pos = cursor;
        if (m_sbuf->pubseekpos(m_start + m_pos, std::ios_base::beg) == std::streampos(-1))
            return std::streampos(-1);

        return m_pos;
    }

    std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
    {
        if (sp < 0 || sp >= m_len)
            return std::streampos(-1);
        m_pos = sp;
        if (m_sbuf->pubseekpos(m_start + m_pos, std::ios_base::beg) == std::streampos(-1))
            return std::streampos(-1);
        return m_pos;
    }

private:
    std::streambuf *m_sbuf;
    std::streampos m_start;
    std::streamsize m_len;
    std::streampos m_pos;
};

Может использоваться следующим образом

using namespace std;

void somefunc(ifstream &bigifs)
{
    substreambuf sbuf(bigifs.rdbuf(),100,100);
    //new istream with the substreambuf as its streambuf
    istream isub(&sbuf);

    //use isub normally
}

Это было вдохновлено Фильтрацией Streambufs

1 голос
/ 04 октября 2011

Все iostreams используют большую часть своей пользовательской логики в своих streambuf специализациях.fstream (или basic_fstream) инициализирует istream экземпляром file_buf.То же самое для stringstream (stringbuf).Если вы хотите развернуть свой собственный поток подпотока, вы можете сделать это, реализовав свой собственный streambuf в терминах родительского потока.

1 голос
/ 02 октября 2011

Я сделал что-то подобное, используя библиотеку Boost.Iostreams .Посмотрите под Учебное пособие | Письменные устройства.Идея состоит в том, чтобы создать класс «device», который реализует низкоуровневый интерфейс (чтение / запись / поиск), а затем создать экземпляр производного класса istream / ostream, используя класс вашего устройства для выполнения фактического ввода-вывода.

0 голосов
/ 04 октября 2011

Просто небольшая идея: если у вас есть контроль над клиентской частью кода (т.е. частью, которая использует входной поток), я предлагаю вам изменить его так, чтобы он принимал два дополнительных параметра, как показано ниже:

// Old code
void ClassUsingInput::SetInput(std::streambuf & inputbuf)
{
   // Implementation ...
}

Может стать:

// New code
void ClassUsingInput::SetInput(std::streambuf & inputbuf, std::streampos position, std::streamsize size) 
{
    inputbuf.pubseekpos(position) ;
    // internally use size to detect end-of-substream
}
...