J'ai un caractère * et la longueur des données que je reçois d'une bibliothèque, et j'ai besoin de passer les données à une fonction qui prend un istream.
Je sais que je peux créer un stringstream mais cela copiera toutes les données. Et aussi, les données auront sûrement des 0 puisqu'il s'agit d'un fichier Zip, et la création d'un flux de chaînes prendra les données jusqu'au premier 0, je pense.
Existe-t-il un moyen de créer un flux à partir d'un caractère * et de sa taille sans copier toutes les données?
Voici une méthode non obsolète trouvée sur le web , avez-vous dérivé votre propre std::streambuf
classe, mais facile et semble fonctionner:
#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;
}
Quelles sorties:
line: I'm a buffer with embedded nullsand line
line: feeds
Une solution non obsolète utilisant 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);
ou encore plus simple:
#include <boost/interprocess/streams/bufferstream.hpp>
using namespace boost::interprocess;
bufferstream input_stream(my_ptr_to_char, byte_count);
J'avais besoin d'une solution prenant en charge tellg
et seekg
et ne nécessitant pas de boost.
char_array_buffer
from n guide du débutant pour écrire un tampon de flux personnalisé (std :: streambuf) a donné un point de départ obtenu.
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_;
}
Le seul moyen (simple) portable consiste à faire la copie:
std::istringstream ss(std::string(buf,len));
En fait, cela est susceptible de copier les données deux fois, une fois pour créer le string
et une fois pour créer le istringstream
. (Peut-être que C++ 11 peut éviter l'une des copies via un constructeur de déplacement; je ne suis pas sûr.)
Cependant, si vous êtes chanceux, votre implémentation C++ vous permettra de faire ceci:
std::istringstream ss;
ss.rdbuf()->pubsetbuf(buf,len);
Sous GNU C++ (et, je crois, d'autres implémentations), cela créera le flux de chaînes sans copier les données. Mais il s'agit d'un comportement "défini par l'implémentation" selon la spécification. (Voir aussi cette question .)
En incluant le paramètre len
, vous vous assurez que les deux n'auront aucun problème avec les caractères nuls.
La seule façon portable de faire ce que vous voulez est d'implémenter votre propre sous-classe de stringbuf
et de l'utiliser pour initialiser le flux de chaînes. Pas pour les faibles de cœur.
Avez-vous essayé std :: istrstream? http://stdcxx.Apache.org/doc/stdlibref/istrstream.html
Techniquement, je pense qu'il est obsolète, mais qu'il fait toujours partie de la norme.