2010-01-17 1 views
27

я не мог найти что-нибудь готовое, так что я придумал:Простой способ создания C++ memystream из (char *, size_t), без копирования данных?

class membuf : public basic_streambuf<char> 
{ 
public: 
    membuf(char* p, size_t n) { 
    setg(p, p, p + n); 
    setp(p, p + n); 
    } 
} 

Использование:

char *mybuffer; 
size_t length; 
// ... allocate "mybuffer", put data into it, set "length" 

membuf mb(mybuffer, length); 
istream reader(&mb); 
// use "reader" 

Я знаю stringstream, но это, кажется, не быть в состоянии работать с двоичными данными заданной длины.

Я придумываю свое колесо здесь?

EDIT

  • Он не должен скопировать входные данные, просто создать что-то, что будет выполнять итерацию над данными.
  • Он должен быть портативным - по крайней мере, он должен работать как под gcc, так и с MSVC.
+0

Какая версия MSVC? > 6, надеюсь. ;) –

+0

MSVC 9.0 aka 2008 –

+1

Я думаю, что ваше решение хорошее. :) http://stackoverflow.com/questions/1448467/initializing-ac-stdistringstream-from-an-in-memory-buffer/1449527#1449527 –

ответ

26

Я предполагаю, что ваш ввод данных в двоичном (не текст), и что вы хотите, чтобы извлечь куски двоичных данных из него. Все без копирования ваших входных данных.

Вы можете комбинировать boost::iostreams::basic_array_source и boost::iostreams::stream_buffer (от Boost.Iostreams) с boost::archive::binary_iarchive (от Boost.Serialization), чтобы иметь возможность использовать удобную экстракцию >> операторы читать куски двоичных данных.

#include <stdint.h> 
#include <iostream> 
#include <boost/iostreams/device/array.hpp> 
#include <boost/iostreams/stream.hpp> 
#include <boost/archive/binary_iarchive.hpp> 

int main() 
{ 
    uint16_t data[] = {1234, 5678}; 
    char* dataPtr = (char*)&data; 

    typedef boost::iostreams::basic_array_source<char> Device; 
    boost::iostreams::stream_buffer<Device> buffer(dataPtr, sizeof(data)); 
    boost::archive::binary_iarchive archive(buffer, boost::archive::no_header); 

    uint16_t word1, word2; 
    archive >> word1 >> word2; 
    std::cout << word1 << "," << word2 << std::endl; 
    return 0; 
} 

С GCC 4.4.1 на AMD64, он выводит:

1234,5678

Boost.Serialization является очень мощным и знает, как сериализовать все основные типы, строки, и даже контейнеры STL. Вы можете легко сделать ваши типы сериализуемыми. См. Документацию. Скрытый где-то в источниках Boost.Serialization является примером портативного двоичного архива, который знает, как выполнить правильную свопинг для вашей машины. Это может быть полезно и вам.

Если вы не нуждаетесь в крутости Boost.Serialization и рад прочитать двоичные данные в FREAD() - тип способ, вы можете использовать basic_array_source более простым способом:

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

int main() 
{ 
    uint16_t data[] = {1234, 5678}; 
    char* dataPtr = (char*)&data; 

    typedef boost::iostreams::basic_array_source<char> Device; 
    boost::iostreams::stream<Device> stream(dataPtr, sizeof(data)); 

    uint16_t word1, word2; 
    stream.read((char*)&word1, sizeof(word1)); 
    stream.read((char*)&word2, sizeof(word2)); 
    std::cout << word1 << "," << word2 << std::endl; 

    return 0; 
} 

I получить тот же результат с этой программой.

+0

А, спасибо. Он выглядит довольно универсальным. –

+0

Великий материал, Эмиль. Может быть, это не «проще», но это более общее. Спасибо! –

+1

Причина, по которой только объекты Fstream имеют понятие двоичного, состоит в том, что текстовые и двоичные режимы являются точно такими же; кроме того, как они обрабатывают конец строки. Что касается действий для потоков, то они ничем не отличаются. То, как вы используете операторы потока, чтобы попытаться прочитать целые числа из потока символов, - это не то, как должны работать операторы потока. –

5

Я не уверен, что вам нужно, но делает ли это то, что вы хотите?

char *mybuffer; 
size_t length; 
// allocate, fill, set length, as before 

std::string data(mybuffer, length); 
std::istringstream mb(data); 
//use mb 
+1

Нет, это усекает строку до первого появления 0x00 байт в буфер. Мне нужно создать поток фиксированной длины, из которого я могу читать двоичные данные, когда у меня есть эти данные в известном месте в памяти. –

+2

Я не понимаю, почему это должно было обрабатывать NUL байты специально. Длина уведомления передается в конструктор std :: string отдельно. – Tronic

+2

Вы уверены? Строковый конструктор, который принимает (char *, size_t), не обрабатывает char * param как строку c-style (null-terminated). Он использует длину, которую вы ему даете. – crmoore

4

Стандартный буфер потока имеет эту функцию.
Создать поток. Получает свой буфер, а затем перематывает его.

#include <sstream> 
#include <iostream> 
#include <algorithm> 
#include <iterator> 

int main() 
{ 
    // Your imaginary buffer 
    char buffer[] = "A large buffer we don't want to copy but use in a stream"; 

    // An ordinary stream. 
    std::stringstream str; 

    // Get the streams buffer object. Reset the actual buffer being used. 
    str.rdbuf()->pubsetbuf(buffer,sizeof(buffer)); 

    std::copy(std::istreambuf_iterator<char>(str), 
       std::istreambuf_iterator<char>(), 
       std::ostream_iterator<char>(std::cout) 
      ); 
} 
+0

Хммм ... Я поставил это на тест, и он, похоже, не работает под MSVC и его libs. Однако он работает на gcc. –

+0

Почему. Что не удается? –

+0

Похоже, что basic_streambuf от MSVC :: pubsetbuf ничего не делает, вообще. http://stackoverflow.com/questions/1494182/setting-the-internal-buffer-used-by-a-standard-stream-pubsetbuf –

2

Вопросник хотел что-то, что не копирует данные, и его решение работает нормально. Мой вклад заключается в том, чтобы немного очистить его, поэтому вы можете просто создать один объект, который является входным потоком для данных в памяти. Я тестировал это, и он работает.

class MemoryInputStream: public std::istream 
    { 
    public: 
    MemoryInputStream(const uint8_t* aData,size_t aLength): 
     std::istream(&m_buffer), 
     m_buffer(aData,aLength) 
     { 
     rdbuf(&m_buffer); // reset the buffer after it has been properly constructed 
     } 

    private: 
    class MemoryBuffer: public std::basic_streambuf<char> 
     { 
     public: 
     MemoryBuffer(const uint8_t* aData,size_t aLength) 
      { 
      setg((char*)aData,(char*)aData,(char*)aData + aLength); 
      } 
     }; 

    MemoryBuffer m_buffer; 
    }; 
+0

Черт - это не работает, потому что seekg и т. Д. Не будут работать по умолчанию - вам нужно переопределить различные функции streambuf. –

 Смежные вопросы

  • Нет связанных вопросов^_^