【问题标题】:bidirectional iterator over file/ifstream文件/ifstream 上的双向迭代器
【发布时间】:2012-02-20 01:19:11
【问题描述】:

我需要一个具有双向迭代器/适配器的输入文件流。

不幸的是,std::ifstream(和类似的)只能与std::istream_iterator 一起使用,这是一种不能向后移动的前向迭代器。 (或者我在这里弄错了吗?)

我可以简单地将整个文件加载到内存中,然后在数组上使用更强大的随机访问迭代器;但是我想避免这种情况,并且只阅读我真正需要的内容。我可能真的只需要文件的一小部分。

我可以使用 C stdio.h 函数以某种方式手动完成,但这会很痛苦。我基本上需要手动实现一个双向迭代器,考虑到它的所有规范。

我正在考虑研究 boost iostream 库,但该手册有点压倒性,我希望有人能帮我实现这个特定目标吗?或者也许有另一个已经存在的库可以完全满足我的需要?

我需要 boost xpressive 库的迭代器来解析我的文件,它期望迭代器可以递增和递减。如果我正在读取的文件被缓冲,我会很好,尽管这不是必需的。

有什么想法吗? 谢谢!

【问题讨论】:

  • 您确定需要双向迭代器吗?如果一个前向迭代器就足够了,Boost.Spirit 已经为您服务了:Supporting Libraries -> The multi pass iterator
  • 能否不缓冲文件的一部分,对其进行操作,将其写入临时文件,然后获取文件的下一部分,等等...??
  • 我认为你不能只对文件进行内存映射吗?当然便携性较差,但它为您提供随机访问并且它只读取您真正需要的文件部分(嗯,这些部分的邻域四舍五入到某个页面大小)。
  • @Steve :这正是我在我的代码中所做的,考虑到Boost.Interprocess 具有内存映射文件功能,可移植性就不再那么重要了。
  • 如果你可以双向迭代,它不会是流

标签: c++ boost iostream


【解决方案1】:

如果我以错误的方向遍历文件,我会开始质疑我的要求。这似乎是一种人为的做事方式,而且很可能在某处发生了严重的混乱。

一旦我确认这确实是要求,我就会意识到我们在这里肯定是在谈论文件,而不是例如。命名管道或套接字。也就是说,可能内存映射至少部分文件。我会用它来创建一个遍历内存的迭代器。由于显然需要迭代器,因此无需涉及流。如果还需要流,我仍然会在自定义流缓冲区中内存映射文件和反向缓冲区。

当实际从头开始读取并且只需要能够在必要时向后移动时,它可能比这更简单:保留已读取数据的缓冲区并在移动结束时将其消耗并可能读取整个文件如果结束迭代器用于向后移动应该解决这个问题。以下代码当然可以向前和向后读取文件,但未经彻底测试:

#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() 方法来实际访问它。

【讨论】:

  • "如果我要以错误的方向遍历文件,我会开始质疑我的要求。" 这些是 Boost.Xpressive 的要求,而不是直接要求OP。
  • 那么原作者在错误的事情上使用Boost.Xpressive!以错误的方式读取文件不会太有效。是否将其用于某些库完全不是重点:读取文件背面的要求似乎存在错误的顺序,我会质疑这是必要的。
  • 抱歉,对文件内容运行正则表达式听起来并不奇怪。使用文件流作为正则表达式的输入可能会被误导,但总体目标肯定是没有问题的。
  • 也许对 xpressive 使用“文件流”是错误的。那我可以用什么代替呢?我需要解决我的问题,而不是我做不到的理由!顺便提一句。命名管道或套接字也可以被缓冲,然后在两个方向上迭代。
  • 仅作记录:创建迭代器应该很简单。在创建新的迭代器时,这绝对是可行的。通用迭代器采用者是一种不同的水壶,当引用的值推动边界时,情况就不同了。也就是说,我也认为迭代器不是完全正确的抽象,因为它们将两个不相关的方面(属性访问和定位)组合到一个抽象中,尽管这两个方面可以独立变化。
【解决方案2】:

既然您已经在使用 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() 作为结束迭代器。

【讨论】:

  • 我一定会查看 mapped_file_source。如果您能提供一个如何使用它来实现我需要的示例,那将非常有帮助?
  • @CygnusX1 只需写boost::iostreams::mapped_file_source file("whatever.bin");,你就有了你的文件,你可以反向迭代,例如reverse_copy(file.data(), file.data()+file.size(), std::ostreambuf_iterator&lt;char&gt;(std::cout)); 或者其实是随机访问。
  • 呃,在你可以从一对指针中反向复制之前,必须先 make_iterator_range,这不是最好的例子。
猜你喜欢
  • 2015-07-13
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
  • 2015-07-14
  • 1970-01-01
相关资源
最近更新 更多