如何从uint8_t向量创建istream?

时间:2017-08-16 20:47:52

标签: c++

我正在将通过网络获取数据的功能添加到以前只读取本地文件的代码中。我正在使用的网络库以vector<uint8_t>的形式发送和接收数据。我希望能够在读取文件后重用处理数据的代码,但该代码需要std :: istream,有没有办法让istream读取矢量数据?它是相同的数据,所以我觉得应该有办法,但我无法找到或找出如何做的代码。

当前代码:

    std::ifstream stream("data.img", std::ios::in | std::ios::binary | std::ios::ate);

    if (!stream.is_open())
    {
        throw std::invalid_argument("Could not open file.");
    }
    // the arg for processData is std::istream
    processData(stream);

网络框架:

    vector<uint8_t> data = networkMessage.data;

    // need some way to create istream from data
    std::istream stream = ?

    processData(stream);
    stream.close();

有没有办法做到这一点,还是我咆哮着错误的树?

5 个答案:

答案 0 :(得分:1)

您可以通过将数据分配到std::string并使用绑定到该std::istringstreamhere来实现此目的(不考虑unsigned charsigned char转换问题):

std::string s((char*)networkMessage.data(),networkMessage.size());
std::istringstream iss(s);

std::istream& stream = iss;
         // ^ Note the reference here.

processData(stream);
stream.close();

答案 1 :(得分:1)

std::basic_istream从关联的std::basic_streambuf派生类中获取其数据。 STL为文件I / O和字符串I / O提供此类,但不为内存I / O或网络I / O提供。

您可以轻松编写(或查找第三方)基于内存的streambuf类,该类使用std::vector作为其底层缓冲区,然后您可以构造使用该std::istream的{​​{1}}记忆streambuf。例如(使用imemstreamthis answer):

std::vector<uint8_t> &data = networkMessage.data;
imemstream stream(reinterpret_cast<const char*>(data.data()), data.size());
processData(stream);

答案 2 :(得分:1)

C ++实际上确实为此提供了一个类-istrstream,您可以像这样使用它:

    vector<uint8_t> data = ...;

    // need some way to create istream from data
    std::istrstream stream(reinterpret_cast<const char*>(data.data()), data.size());

    processData(stream);

据我所知,这不会复制数据,这与其他答案不同。但是,它在C ++ 98 because it's hard to know who is responsible for freeing the buffer中也已弃用,因此您可能需要编写自己的等效文件。

答案 3 :(得分:0)

这适用于任何类型的矢量,而不仅仅是uint8_t

STD

template <class T>
auto make_istringstream_std_1(const std::vector<T>& v) -> std::istringstream
{
    using namespace std::string_literals;
    std::string str;

    for (auto& e : v)
    {
        str += std::to_string(e) + " "s;
    }
    // the trailing space is not an issue

    return std::istringstream{str};
}

标准算法

template <class T>
auto make_istringstream_std_2(const std::vector<T>& v) -> std::istringstream
{
    std::stringstream ss;
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(ss, " "));
    // the trailing space is not an issue

    return std::istringstream{ss.str()};
}

升压

template <class T>
auto make_istringstream_boost(const std::vector<T>& v) -> std::istringstream
{
    using boost::adaptors::transformed;
    using boost::algorithm::join;

    return std::istringstream{
        join(v | transformed([](int a) { return std::to_string(a); }), " ")};
}

归属:

How to transform a vector<int> into a string?

A good example for boost::algorithm::join

答案 4 :(得分:0)

  1. istream 是原始数据的引用。它不保存数据,而只是一个访问者,通过保留一些 char* 数据内存地址的开始和结束指针。

  2. vector<>中的存储是连续的,但是通过使用push_back(),存储地址可能会发生变化,(复制内部vector)

  3. 因此可以将 istream 变成 const vector

参考

https://en.cppreference.com/w/cpp/io/basic_istream https://www.cplusplus.com/reference/streambuf/streambuf/

最短的例子

class vectorbuf : public std::streambuf {
public:
    vectorbuf(std::vector<uint8_t> &v){
        setg((char*)v.data(), (char*)v.data(), (char*)(v.data() + v.size()));
    }
    ~vectorbuf() {}
};

//Usage:
vector<uint8_t>  arr{11,12,13,14,15,16};
vectorbuf vbuf(arr);
std::istream is(&vbuf);

完整的错误示例代码

#include <streambuf>
#include <iostream>
#include <iomanip>
#include <vector>

using namespace std;


template<typename T>
class vectorbuf : public std::streambuf {
public:
    vectorbuf(std::vector<T> &v) : _value(v) {
        char *bptr = (char*)_value.data();
        char *eptr = (char*)(_value.data() + _value.size());
        setg(bptr, bptr, eptr);
        cout<<"Setg: "<<(void*)bptr<<" "<<(void*)eptr<<endl;
    }
    ~vectorbuf() {}

//Zone start ---
//Note: this zone of code can be commented since the virtual function in base class do same 
protected:
    virtual int underflow() {
        char *bptr = (char*)_value.data();
        char *new_eptr = (char*)(_value.data() + _value.size());
        cout<<"[underflow() when gptr()="<<(void*)gptr()
            <<", now_bptr="<<(void*)bptr<<" now_eptr="<<(void*)new_eptr<<"]";

        return traits_type::eof();
        //since the vector& must not modified, the code below is unnecessary.
        if (new_eptr == egptr())
            return traits_type::eof();
        setg(bptr, gptr(), new_eptr);
        return *gptr();
    }
//Zone end ---

private:
    std::vector<T> &_value;
};


int main() {
    vector<int>  arr{'a',12,13,14,15};
    cout<<"The array: ";
    for (int i=0; i<arr.size(); i++)
        cout<<arr[i]<<" ";
    cout<<endl;
    cout<<"  storage: ";
    for (int i=0; i<arr.size()*sizeof(int); i++) {
        char *ptr = (char*)arr.data();
        cout<<static_cast<int>(ptr[i])<<" ";
    }
    cout<<endl;

    vectorbuf<int> vbuf(arr);
    std::istream is(&vbuf);
    arr.push_back(16); //!!! wrong code here !!!

    //the size of arr is 6*4 == 24, with sizeof(int)==4
    for (int i=0; i<26; i++) {
        cout<<"good?"<<is.good()
            <<", fail?"<<is.fail()
            <<", bad?"<<is.bad()
            <<", eof?"<<is.eof()
            <<", tellg="<<is.tellg();

        //Note there must be char
        //'int a' would not accepted and make is.fail() to true
        //and std::noskipws is also importanted
        char a;
        is>>std::noskipws>>a;
        int out = a;
        cout<<", Read from arr: "<<out<<endl;
    }
    return 0;
}