我有一个char *和我从库中收到的数据长度,我需要将数据传递给一个带有istream的函数。
我知道我可以创建一个字符串流但是会复制所有数据。而且,数据肯定会有0,因为它是一个zip文件,创建一个字符串流将把数据带到我认为的第一个0。
有没有办法从char *创建一个istream而且它的大小没有复制所有数据?
答案 0 :(得分:61)
这是一个不推荐使用的方法found on the web,您是否派生了自己的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\0and 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;
}
哪个输出:
line: I'm a buffer with embedded nullsand line
line: feeds
答案 1 :(得分:11)
使用Boost的非弃用解决方案:
#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);
甚至更简单:
#include <boost/interprocess/streams/bufferstream.hpp>
using namespace boost::interprocess;
bufferstream input_stream(my_ptr_to_char, byte_count);
答案 2 :(得分:6)
我需要一个支持tellg
和seekg
的解决方案,并且不需要提升。
char_array_buffer
给出了一个起点。
byte_array_buffer.h:
#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_;
};
byte_array_buffer.cpp:
#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_;
}
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_;
}
答案 3 :(得分:5)
唯一(简单)便携方式包括制作副本:
std::istringstream ss(std::string(buf,len));
实际上,这可能会复制数据两次,一次创建string
,一次创建istringstream
。 (也许C ++ 11可以通过移动构造函数避免其中一个副本;我不确定。)
但是,如果你很幸运,你的C ++实现将允许你这样做:
std::istringstream ss;
ss.rdbuf()->pubsetbuf(buf,len);
在GNU C ++下(我相信,其他一些实现),这将创建字符串流而不复制数据。但这是根据规范的“实现定义”行为。 (另见this question。)
通过包含len
参数,可以确保这两个参数都没有问题。
执行所需操作的唯一可移植方法是实现自己的stringbuf
子类并使用它来初始化stringstream。不适合胆小的人。
答案 4 :(得分:1)
你试过std :: istrstream吗? http://stdcxx.apache.org/doc/stdlibref/istrstream.html
从技术上讲,我认为它已被弃用,但仍然是标准的一部分。
答案 5 :(得分:1)
已接受答案的扩展,它支持tellg和seekg:
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);
}
};
此类的用法保持不变。
答案 6 :(得分:-1)
尝试使用Boost.Iostreams数组源和接收类。
http://www.boost.org/doc/libs/1_47_0/libs/iostreams/doc/index.html