【问题标题】:Is there a way to create a stringstream from a string_view without copying data?有没有办法在不复制数据的情况下从 string_view 创建字符串流?
【发布时间】:2019-10-23 14:10:38
【问题描述】:

我认为这是一个非常直截了当的问题。我特别想使用std::get_time,但它需要某种流才能使用。我在 string_view 中传递数据,并希望避免复制它只是为了解析日期。

【问题讨论】:

  • 我现在可以看到它,来到 C++23:std::basic_string_view_stream ;-) 不确定是否可以避免复制。也许霍华德会知道一招。
  • 早已弃用的std::strstream 可以做到这一点。
  • 我错误地标记了这个 C++14,因为这就是我们在这个项目中的内容。我实际上忘记了我们使用的是boost::string_view。不过,这个问题仍然有效。

标签: c++ c++17 stringstream


【解决方案1】:

您可以使用 Boost.Iostreams 库轻松做到这一点:

#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

#include <iostream>
#include <string>

int main() {
    std::string_view buf{"hello\n"};
    boost::iostreams::stream<boost::iostreams::basic_array_source<char>> stream(buf.begin(), buf.size());

    std::string s;
    stream >> s;
    std::cout << s << '\n';
}

您应该能够使用 std::stringstreamstd::basic_stringbuf&lt;CharT,Traits,Allocator&gt;::setbuf 做到这一点,但 C++ 标准搞砸了它的要求:

[setbuf] 的效果是实现定义的:一些实现什么都不做,而一些实现清除当前用作缓冲区的std::string 成员并开始使用用户提供的大小为n 的字符数组,其第一个元素由s 指向,作为缓冲区和输入/输出字符序列。

【讨论】:

    【解决方案2】:

    另一种解决方案,没有 Boost.IOstream(当你不能使用它时)。它基于扩展 std::basic_streambuf 和 std::basic_istream。

    #include <cstring>
    #include <iostream>
    #include <istream>
    #include <string_view>
    
    template<typename __char_type, class __traits_type >
    class view_streambuf final: public std::basic_streambuf<__char_type, __traits_type > {
    private:
        typedef std::basic_streambuf<__char_type, __traits_type > super_type;
        typedef view_streambuf<__char_type, __traits_type> self_type;
    public:
    
        /**
        *  These are standard types.  They permit a standardized way of
        *  referring to names of (or names dependent on) the template
        *  parameters, which are specific to the implementation.
        */
        typedef typename super_type::char_type char_type;
        typedef typename super_type::traits_type traits_type;
        typedef typename traits_type::int_type int_type;
        typedef typename traits_type::pos_type pos_type;
        typedef typename traits_type::off_type off_type;
    
        typedef typename std::basic_string_view<char_type, traits_type> source_view;
    
        view_streambuf(const source_view& src) noexcept:
          super_type(),
          src_( src )
        {
            char_type *buff = const_cast<char_type*>( src_.data() );
            this->setg( buff , buff, buff + src_.length() );
        }
    
        virtual std::streamsize xsgetn(char_type* __s, std::streamsize __n) override
        {
            if(0 == __n)
                return 0;
            if( (this->gptr() + __n) >= this->egptr() ) {
                __n =  this->egptr() - this->gptr();
                if(0 == __n && !traits_type::not_eof( this->underflow() ) )
                    return -1;
            }
            std::memmove( static_cast<void*>(__s), this->gptr(), __n);
            this->gbump( static_cast<int>(__n) );
            return __n;
        }
    
        virtual int_type pbackfail(int_type __c) override
        {
            char_type *pos = this->gptr() - 1;
            *pos = traits_type::to_char_type( __c );
            this->pbump(-1);
            return 1;
        }
    
        virtual int_type underflow() override
        {
            return traits_type::eof();
        }
    
        virtual std::streamsize showmanyc() override
        {
            return static_cast<std::streamsize>( this->egptr() - this->gptr() );
        }
    
        virtual ~view_streambuf() override
        {}
    private:
        const source_view& src_;
    };
    
    template<typename _char_type>
    class view_istream final:public std::basic_istream<_char_type, std::char_traits<_char_type> > {
        view_istream(const view_istream&) = delete;
        view_istream& operator=(const view_istream&) = delete;
    private:
        typedef std::basic_istream<_char_type, std::char_traits<_char_type> > super_type;
        typedef view_streambuf<_char_type, std::char_traits<_char_type> > streambuf_type;
    public:
        typedef _char_type  char_type;
        typedef typename super_type::int_type int_type;
        typedef typename super_type::pos_type pos_type;
        typedef typename super_type::off_type off_type;
        typedef typename super_type::traits_type traits_type;
        typedef typename streambuf_type::source_view source_view;
    
        view_istream(const source_view& src):
            super_type( nullptr ),
            sb_(nullptr)
        {
            sb_ = new streambuf_type(src);
            this->init( sb_ );
        }
    
    
        view_istream(view_istream&& other) noexcept:
            super_type( std::forward<view_istream>(other) ),
            sb_( std::move( other.sb_ ) )
        {}
    
        view_istream& operator=(view_istream&& rhs) noexcept
        {
            view_istream( std::forward<view_istream>(rhs) ).swap( *this );
            return *this;
        }
    
        virtual ~view_istream() override {
            delete sb_;
        }
    
    private:
        streambuf_type *sb_;
    };
    
    
    int main(int argc, const char** argv)
    {
        std::string_view v("ABDCD\n 123\n FAG\n 456789");
    
        view_istream<char> in( v );
    
        std::string s;
        in >> s;
        std::cout << s << std::endl;
    
        int i;
        in >> i;
        std::cout << i << std::endl;
    
        in >> s;
        std::cout << s << std::endl;
    
        in >> i;
        std::cout << i << std::endl;
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-16
      • 2019-11-30
      • 1970-01-01
      • 2012-07-25
      相关资源
      最近更新 更多