如何为boost :: tokenizer实现tokenizer.rbegin()和rend()?

时间:2011-01-05 22:30:10

标签: c++ boost

我正在使用boost :: tokenizer但是我意识到它不支持rbegin()和rend()。我想问一下如何将这两个函数添加到现有类中?

这是来自提升网站:

#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main() {
 string str( "12/12/1986" );
 typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
 boost::char_separator<char> sep( "/" );
 tokenizer tokens( str, sep );
 cout << *tokens.begin() << endl;
    // cout << *tokens.rbegin() << endl; How could I implement this?
 return 0;
}

更新
这是我目前的进展。我首先导航到tokenizer的src:

//===========================================================================
  // A container-view of a tokenized "sequence"
  template <
    typename TokenizerFunc = char_delimiters_separator<char>, 
    typename Iterator = std::string::const_iterator,
    typename Type = std::string
  >
  class tokenizer {
  private:
    typedef token_iterator_generator<TokenizerFunc,Iterator,Type> TGen;

    // It seems that MSVC does not like the unqualified use of iterator,
    // Thus we use iter internally when it is used unqualified and
    // the users of this class will always qualify iterator.     
    typedef typename TGen::type iter;

  public:

    typedef iter iterator;
    typedef iter const_iterator;
    typedef Type value_type;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef value_type* pointer;
    typedef const pointer const_pointer;
    typedef void size_type;
    typedef void difference_type;

    tokenizer(Iterator first, Iterator last,
              const TokenizerFunc& f = TokenizerFunc()) 
      : first_(first), last_(last), f_(f) { }

    template <typename Container>
    tokenizer(const Container& c)
      : first_(c.begin()), last_(c.end()), f_() { }

    template <typename Container>
    tokenizer(const Container& c,const TokenizerFunc& f)
      : first_(c.begin()), last_(c.end()), f_(f) { }

    void assign(Iterator first, Iterator last){
      first_ = first;
      last_ = last;
    }

    void assign(Iterator first, Iterator last, const TokenizerFunc& f){
      assign(first,last);
      f_ = f;
    }

    template <typename Container>
    void assign(const Container& c){
      assign(c.begin(),c.end());
    }


    template <typename Container>
    void assign(const Container& c, const TokenizerFunc& f){
      assign(c.begin(),c.end(),f);
    }

    iter begin() const { return iter(f_,first_,last_); }
    iter end() const { return iter(f_,last_,last_); }

  private:
    Iterator first_;
    Iterator last_;
    TokenizerFunc f_;
  };

然后我专注于两种方法:

iter begin() const { return iter(f_,first_,last_); }
iter end() const { return iter(f_,last_,last_); }

因为它返回一个iter类型的构造函数(f_,first_,last_),然后我移动到这个类的源代码。事实上是:

 typedef typename TGen::type iter;

这是token_iterator_generator类。这个类的实现是:

template <
        class TokenizerFunc = char_delimiters_separator<char>, 
        class Iterator = std::string::const_iterator,
        class Type = std::string
    >
    class token_iterator_generator {

    private: 
    public:
        typedef token_iterator<TokenizerFunc,Iterator,Type> type;
    };

所以现在我发现Tokenizer类的迭代器实际上是token_iterator&lt;&gt;。而token_iterator的实现确实吓坏了我:

template <class TokenizerFunc, class Iterator, class Type>
  class token_iterator
      : public iterator_facade<
            token_iterator<TokenizerFunc, Iterator, Type>
          , Type
          , typename detail::minimum_category<
                forward_traversal_tag
              , typename iterator_traversal<Iterator>::type
            >::type 
          , const Type&
        >
  {

      friend class iterator_core_access;

      TokenizerFunc f_;
      Iterator begin_;
      Iterator end_;
      bool valid_;
      Type tok_;

      void increment(){
          BOOST_ASSERT(valid_);
          valid_ = f_(begin_,end_,tok_);
      }

      const Type&  dereference() const {
          BOOST_ASSERT(valid_);
          return tok_;
      }
      template<class Other>
      bool equal(const Other& a) const{
          return (a.valid_ && valid_)
              ?( (a.begin_==begin_) && (a.end_ == end_) )
              :(a.valid_==valid_);

      }

      void initialize(){
          if(valid_) return;
          f_.reset();
          valid_ = (begin_ != end_)?
              f_(begin_,end_,tok_):false;
      }
  public:
      token_iterator():begin_(),end_(),valid_(false),tok_() { }

      token_iterator(TokenizerFunc f, Iterator begin, Iterator e = Iterator())
          : f_(f),begin_(begin),end_(e),valid_(false),tok_(){ initialize(); }

      token_iterator(Iterator begin, Iterator e = Iterator())
            : f_(),begin_(begin),end_(e),valid_(false),tok_() {initialize();}

      template<class OtherIter>
      token_iterator(
            token_iterator<TokenizerFunc, OtherIter,Type> const& t
            , typename enable_if_convertible<OtherIter, Iterator>::type* = 0)
            : f_(t.tokenizer_function()),begin_(t.base())
            ,end_(t.end()),valid_(!t.at_end()),tok_(t.current_token()) {}

      Iterator base()const{return begin_;}

      Iterator end()const{return end_;};

      TokenizerFunc tokenizer_function()const{return f_;}

      Type current_token()const{return tok_;}

      bool at_end()const{return !valid_;}




  };

这个类非常复杂,我迷失在这里:(!来自iterator_facade&lt;&gt;的继承部分非常复杂。 我应该接下来的任何想法吗?

谢谢,

3 个答案:

答案 0 :(得分:1)

您当然可以通过使用普通的标记化器简单地迭代字符串并将值存储在向量中来创建一个实现此目的的新类。然后使用向量实现rend和rbegin。

发生错误的风险很小而且几乎没有,尽管可能不是运行时最快或内存最少的内存。除非你知道这将成为一个问题,否则这就是我要采取的路线,因为它很容易实现。

你特别要求扩展现有的课程,我认为这是一个坏主意。在这种情况下,最简单的可能是更改构造函数,该构造函数使用容器(您正在使用的容器)使用rbegin()和rend()而不是容器的begin()和end()来实例化内部标记化器对象。要实现自己的rbegin()和rend(),请从内部标记生成器的begin和end开始使用迭代器。从这些返回的令牌可能会倒退,所以你需要扭转这些令牌。实际的迭代器类型可能很容易使用boost :: iterators包实现。

请注意,您需要特别注意采用显式迭代器的构造函数,或者只决定为反向迭代器实现类的功能的子集(在这种情况下,您应该使用一个保持两个的单独类)无论如何,不​​要伪装成boost :: tokenizer,而是假装内部标记器。

替代方法是更改​​char_separator(和其他分隔符类),以便您可以显式指定tokenizer_detail :: assign_or_plus_equal的哪个特化,以便您可以在每个部分字符串的开头添加每个字符而不是端。

希望这会有所帮助。我会选择第一个替代方案,或者只是简单地改变我的要求,以便我不需要反向迭代器。

答案 1 :(得分:0)

考虑到这一点(来自the documentation

  

tokenizer类提供序列中包含的一系列标记的容器视图。您可以将序列设置为parse,并使用TokenizerFunction在构造时或使用assign成员函数来解析序列。注意:在构造时实际上没有进行解析。解析是按需完成的,因为通过begin提供的迭代器访问令牌。

我会说你几乎可以复制粘贴代码,但是从str [strlen(str)-1]开始直到0.但是,首先必须创建一个名为 reverse_iterator 的typedef并且当反向迭代器到达字符串的开头时,实际上按需解析标记。在你这样做的时候,不要犹豫,展示你的进步并提出问题。

答案 2 :(得分:0)

我建议使用boost::algorithm::split

#include <boost/algorithm/string/split.hpp>

std::string str("12/12/1986");
std::vector<std::string> results;

boost::algorithm::split(results, str, boost::algorithm::is_any_of("/"));

(见Boost String Algorithms Library

因此,您可以使用rbegin()和rend()轻松迭代生成的std :: vector。