C++ 从字符中获取 istream*

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7781898/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:29:05  来源:igfitidea点击:

Get an istream from a char*

c++istream

提问by Damian

I have a char* and the data length that I'm receiving from a library, and I need to pass the data to a function that takes an istream.

我有一个 char* 和我从库接收的数据长度,我需要将数据传递给一个接受 istream 的函数。

I know I can create a stringstream but that will copy all the data. And also, the data will surely have 0s since it's a zip file, and creating a stringstream will take the data until the first 0 I think.

我知道我可以创建一个字符串流,但这会复制所有数据。而且,数据肯定会有 0,因为它是一个 zip 文件,创建 stringstream 会将数据带到我认为的第一个 0。

Is there any way to create an istream from a char* and it's size without copying all the data?

有什么方法可以在不复制所有数据的情况下从 char* 创建 istream 及其大小?

回答by HostileFork says dont trust SE

Here's a non-deprecated method found on the web, has you derive your own std::streambufclass, but easy and seems to work:

这是在网上找到的一个未弃用的方法,您是否派生了自己的std::streambuf类,但很容易并且似乎有效:

#include <iostream>
#include <istream>
#include <streambuf>
#include <string>

struct membuf : std::streambuf
{
    membuf(char* begin, char* end) {
        this->setg(begin, begin, end);
    }
};

int main()
{
    char buffer[] = "I'm a buffer with embedded nulls
line: I'm a buffer with embedded nullsand line
line:  feeds
and line\n feeds"; membuf sbuf(buffer, buffer + sizeof(buffer)); std::istream in(&sbuf); std::string line; while (std::getline(in, line)) { std::cout << "line: " << line << "\n"; } return 0; }

Which outputs:

哪些输出:

#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/array.hpp>
using namespace boost::iostreams;

basic_array_source<char> input_source(my_ptr_to_char, byte_count);
stream<basic_array_source<char> > input_stream(input_source);

回答by ZunTzu

A non deprecated solution using Boost:

使用 Boost 的非弃用解决方案:

#include <boost/interprocess/streams/bufferstream.hpp>
using namespace boost::interprocess;

bufferstream input_stream(my_ptr_to_char, byte_count);

or even simpler:

甚至更简单:

#include <cstdio>
#include <string>
#include <list>
#include <fstream>
#include <iostream>

//
// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
//

class byte_array_buffer : public std::streambuf
{
public:
    byte_array_buffer(const uint8_t *begin, const size_t size);

private:
    int_type underflow();
    int_type uflow();
    int_type pbackfail(int_type ch);
    std::streamsize showmanyc();
    std::streampos seekoff ( std::streamoff off, std::ios_base::seekdir way,
                            std::ios_base::openmode which = std::ios_base::in | std::ios_base::out );
    std::streampos seekpos ( std::streampos sp,
                            std::ios_base::openmode which = std::ios_base::in | std::ios_base::out);

    // copy ctor and assignment not implemented;
    // copying not allowed
    byte_array_buffer(const byte_array_buffer &);
    byte_array_buffer &operator= (const byte_array_buffer &);

private:
    const uint8_t * const begin_;
    const uint8_t * const end_;
    const uint8_t * current_;
};

回答by catlan

I needed a solution that supports tellgand seekgand didn't require boost.

我需要一个支持tellg并且seekg不需要提升的解决方案。

char_array_bufferfrom A beginner's guide to writing a custom stream buffer (std::streambuf)gave a got starting point.

char_array_buffer来自编写自定义流缓冲区的初学者指南 (std::streambuf)给出了一个起点。

byte_array_buffer.h:

byte_array_buffer.h:

#include "byte_array_buffer.h"

#include <cassert>


byte_array_buffer::byte_array_buffer(const uint8_t *begin, const size_t size) :
begin_(begin),
end_(begin + size),
current_(begin_)
{
    assert(std::less_equal<const uint8_t *>()(begin_, end_));
}

byte_array_buffer::int_type byte_array_buffer::underflow()
{
    if (current_ == end_)
        return traits_type::eof();

    return traits_type::to_int_type(*current_);
}

byte_array_buffer::int_type byte_array_buffer::uflow()
{
    if (current_ == end_)
        return traits_type::eof();

    return traits_type::to_int_type(*current_++);
}

byte_array_buffer::int_type byte_array_buffer::pbackfail(int_type ch)
{
    if (current_ == begin_ || (ch != traits_type::eof() && ch != current_[-1]))
        return traits_type::eof();

    return traits_type::to_int_type(*--current_);
}

std::streamsize byte_array_buffer::showmanyc()
{
    assert(std::less_equal<const uint8_t *>()(current_, end_));
    return end_ - current_;
}


std::streampos byte_array_buffer::seekoff ( std::streamoff off, std::ios_base::seekdir way,
                                           std::ios_base::openmode which )
{
    if (way == std::ios_base::beg)
    {
        current_ = begin_ + off;
    }
    else if (way == std::ios_base::cur)
    {
        current_ += off;
    }
    else if (way == std::ios_base::end)
    {
        current_ = end_ + off;
    }

    if (current_ < begin_ || current_ > end_)
        return -1;


    return current_ - begin_;
}

std::streampos byte_array_buffer::seekpos ( std::streampos sp,
                                           std::ios_base::openmode which )
{
    current_ = begin_ + sp;

    if (current_ < begin_ || current_ > end_)
        return -1;

    return current_ - begin_;
}

byte_array_buffer.cpp:

byte_array_buffer.cpp:

std::istringstream ss(std::string(buf,len));

回答by Nemo

The only (simple) portable way includes making the copy:

唯一的(简单的)便携方式包括制作副本:

std::istringstream ss;
ss.rdbuf()->pubsetbuf(buf,len);

In fact, this is likely to copy the data twice, once to create the stringand once to create the istringstream. (Maybe C++11 can avoid one of the copies via a move constructor; I am not sure.)

实际上,这很可能会复制数据两次,一次是创建string,一次是创建istringstream. (也许 C++11 可以通过移动构造函数避免其中一个副本;我不确定。)

However, if you are lucky, your C++ implementation will let you do this:

但是,如果幸运的话,你的 C++ 实现会让你这样做:

struct membuf : std::streambuf
{
    membuf(char* begin, char* end)
    {
        this->setg(begin, begin, end);
    }

    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override
    {
        if (dir == std::ios_base::cur)
            gbump(off);
        else if (dir == std::ios_base::end)
            setg(eback(), egptr() + off, egptr());
        else if (dir == std::ios_base::beg)
            setg(eback(), eback() + off, egptr());
        return gptr() - eback();
    }

    pos_type seekpos(pos_type sp, std::ios_base::openmode which) override
    {
        return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
    }
};

Under GNU C++ (and, I believe, some other implementations), this will create the stringstream without copying the data. But this is "implementation-defined" behavior according to the spec. (See also this question.)

在 GNU C++ 下(我相信还有其他一些实现),这将创建字符串流而不复制数据。但根据规范,这是“实现定义的”行为。(另见这个问题。)

By including the lenparameter, you ensure that both of these will have no problem with null characters.

通过包含len参数,您可以确保这两个参数都不会出现空字符问题。

The only portable way to do what you want is to implement your own subclass of stringbufand use it to initialize the stringstream. Not for the faint of heart.

做你想做的唯一可移植的方法是实现你自己的子类stringbuf并使用它来初始化字符串流。不适合胆小的人。

回答by Vincent Wen

An extension to the accepted answer that supports tellg and seekg:

支持 tellg 和 seekg 的已接受答案的扩展:

##代码##

Usage of this class stays the same.

这个类的用法保持不变。

回答by jeffrey_t_b

Have you tried std::istrstream? http://stdcxx.apache.org/doc/stdlibref/istrstream.html

你试过 std::istrstream 吗? http://stdcxx.apache.org/doc/stdlibref/istrstream.html

Technically, I think that it is deprecated, but still part of the standard.

从技术上讲,我认为它已被弃用,但仍是标准的一部分。

回答by Benjamin Lindley

Try the Boost.Iostreams array source and sink classes.

试试 Boost.Iostreams 数组源和接收器类。

http://www.boost.org/doc/libs/1_47_0/libs/iostreams/doc/index.html

http://www.boost.org/doc/libs/1_47_0/libs/iostreams/doc/index.html