двунаправленный итератор над файлом / ifstream - PullRequest
10 голосов
/ 27 января 2012

Мне нужен поток входных файлов, который будет иметь двунаправленный итератор / адаптер.

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

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

Я мог бы как-то сделать это вручную, используя функции C stdio.h, но это будет больно. В основном мне нужно было бы реализовать двунаправленный итератор, учитывая всю его специфику, вручную.

Я подумываю изучить библиотеку boost iostream, но руководство несколько перегружено, я надеялся, что кто-нибудь сможет мне помочь в достижении этой конкретной цели? Или, может быть, есть другая уже существующая библиотека, которая делает именно то, что мне нужно?

Мне нужен итератор для библиотеки boost xpressive для анализа моих файлов, который ожидает, что итератор может быть увеличен или уменьшен. Было бы хорошо, если файл, который я читаю, буферизован, хотя это не является обязательным требованием.

Есть идеи? Спасибо!

Ответы [ 2 ]

5 голосов
/ 27 января 2012

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

Как только я подтвердил, что это действительно требование, я понял, что мы определенно говорим здесь, а не, например. именованная труба или сокет. То есть была бы возможна карта памяти хотя бы части файла. Я хотел бы использовать это для создания итератора, который обходит память. Поскольку, очевидно, нужен итератор, нет необходимости привлекать потоки. Если бы понадобились и потоки, я бы все равно использовал карту памяти и обратные буферы со спины в пользовательском буфере потоков.

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

#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <limits>
#include <vector>

class bidirectional_stream
{
public:
    class                                         iterator;
    typedef iterator                              const_iterator;
    typedef std::reverse_iterator<iterator>       reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

    bidirectional_stream(std::istream& in):
        in_(in)
    {
    }
    iterator         begin();
    iterator         end();
    reverse_iterator rbegin();
    reverse_iterator rend();

    bool expand()
    {
        char buffer[1024];
        this->in_.read(buffer, sizeof(buffer));
        this->buffer_.insert(this->buffer_.end(), buffer, buffer + this->in_.gcount());
        return 0 < this->in_.gcount();
    }
    long read_all()
    {
        this->buffer_.insert(this->buffer_.end(),
                             std::istreambuf_iterator<char>(this->in_),
                             std::istreambuf_iterator<char>());
        return this->buffer_.size();
    }
    char get(long index) { return this->buffer_[index]; }
    long current_size() const { return this->buffer_.size(); }
private:
    std::istream&     in_;
    std::vector<char> buffer_;
};

class bidirectional_stream::iterator
{
public:
    typedef char                            value_type;
    typedef char const*                     pointer;
    typedef char const&                     reference;
    typedef long                            difference_type;
    typedef std::bidirectional_iterator_tag iterator_category;

    iterator(bidirectional_stream* context, size_t pos):
        context_(context),
        pos_(pos)
    {
    }

    bool operator== (iterator const& other) const
    {
        return this->pos_ == other.pos_
            || (this->pos_ == this->context_->current_size()
                && !this->context_->expand()
                && other.pos_ == std::numeric_limits<long>::max());
    }
    bool operator!= (iterator const& other) const { return !(*this == other); }
    char      operator*() const { return this->context_->get(this->pos_); }
    iterator& operator++()    { ++this->pos_; return *this; }
    iterator  operator++(int) { iterator rc(*this); this->operator++(); return rc; }
    iterator& operator--()
    {
        if (this->pos_ == std::numeric_limits<long>::max())
        {
            this->pos_ = this->context_->read_all();
        }
        --this->pos_;
        return *this;
    }
    iterator  operator--(int) { iterator rc(*this); this->operator--(); return rc; }

private:
    bidirectional_stream* context_;
    long                  pos_;
};

bidirectional_stream::iterator bidirectional_stream::begin()
{
    return iterator(this, 0);
}
bidirectional_stream::iterator bidirectional_stream::end()
{
    return iterator(this, std::numeric_limits<long>::max());
}

bidirectional_stream::reverse_iterator bidirectional_stream::rbegin()
{
    return reverse_iterator(this->end());
}
bidirectional_stream::reverse_iterator bidirectional_stream::rend()
{
    return reverse_iterator(this->begin());
}

Просто создайте bidirectional_stream с потоком, который вы хотите прочитать в качестве аргумента, а затем используйте методы begin() и end() для фактического доступа к нему.

3 голосов
/ 27 января 2012

Поскольку вы уже используете boost, взгляните на boost::iostreams::mapped_file_source http://www.boost.org/doc/libs/release/libs/iostreams/doc/classes/mapped_file.html#mapped_file_source

. Вы можете использовать file.data () в качестве начального итератора и file.data () + file.size () как конечный итератор.

...