Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
196 views
in Technique[技术] by (71.8m points)

c++ - bidirectional iterator over file/ifstream

I need an input file stream which would have a bidirectional iterator/adapter.

Unfortunately std::ifstream (and similar) can be used only with std::istream_iterator which is a kind of forward iterator which cannot go backwards. (or am I mistaken here?)

I could simply load the whole file to memory and then use a much more powerful random-access iterator over the array; however I would like to avoid that, and read only as much as I really need. It may happen that I really need only a small portion of a file.

I could somehow do it manually using C stdio.h functions, but that will be painful. I would basically need to implement a bidirectional iterator, with all its specification in mind, by hand.

I am considering looking into boost iostream library, but the manual is somewhat overwhelming, I was hoping someone could give me a hand to achieve this particular goal? Or maybe there is another already existing library to do exactly what I need?

I need the iterator for the boost xpressive library to parse my files, which expects that the iterator can be incremented as well as decremented. I would be fine if the file I am reading is buffered, although this is not a requirement.

Any ideas? Thank you!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

If I were to traverse a file in the wrong direction, I would start off questioning my requirements. This seems to be a contrived way to do things and most likely something got messed up dramatically somewhere.

Once I confirmed that this is indeed the requirement I would realize that we are definitely talking files here, rather than e.g. a named pipe or a socket. That is, it would be possible memory map at least parts of the file. I would use this to create an iterator which walks the memory. Since obviously an iterator is needed, there is no need to involve streams. If streams were needed too, I would still memory map the file and reverse buffers from the back in a custom stream buffer.

When actually reading from the start and just needing to be able to move backwards when necessary, it may be simpler than this: keeping a buffer of the already read data and expending it when moving off the end plus possibly reading the entire file if the end iterator is used to move backwards should address this. Here is code which certainly can read a file forward and backward but isn't thoroughly tested:

#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());
}

Just create a bidirectional_stream with the stream you want to read as an argument and then use the begin() and end() methods to actually access it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...