2010-01-17 30 views
27

我找不到任何现成的,所以我想出了:从(char *,size_t)创建C++内存流的简单方法,无需复制数据?

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,但它似乎并没有能够工作与给定长度的二进制数据。

我在这里发明了自己的车轮吗?

编辑

  • 不得复制输入数据,刚刚创造的东西,会遍历数据。
  • 它必须是可移植的 - 至少它应该同时在gcc和MSVC下工作。
+0

什么版本的MSVC? > 6,我希望。 ;) – 2010-01-17 06:15:24

+0

MSVC 9.0又名2008 – 2010-01-17 06:21:16

+1

我认为你的解决方案很好。 :) http://stackoverflow.com/questions/1448467/initializing-ac-stdistringstream-from-an-in-memory-buffer/1449527#1449527 – 2010-01-17 10:55:28

回答

26

我假设你的输入数据是二进制(不是文本),并且你想从中提取二进制数据块。所有这些都不需要复制输入数据。

您可以结合boost::iostreams::basic_array_sourceboost::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; 
} 

随着AMD64 GCC 4.4.1,它输出:

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; 
} 

我用这个程序得到相同的输出。

+0

啊,谢谢。它看起来很普遍。 – 2010-01-17 05:32:40

+0

伟大的东西,埃米尔。也许它不是“更简单”,但它肯定更通用。谢谢! – 2010-01-17 08:13:36

+1

只有fstream对象具有二进制的概念的原因是文本和二进制模式完全相同;除了他们如何处理行尾。就流操作而言,他们没有什么不同。您使用流操作符尝试从char流读取整数的方式不是流操作符应该如何工作的方式。 – 2010-01-17 09:44:43

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字节在缓冲。我特别需要创建一个可以读取二进制数据的固定长度的数据流,当我将这些数据存储在内存中的已知位置时。 – 2010-01-17 04:30:56

+2

我不明白为什么会专门处理NUL字节。注意长度分别传递给std :: string构造函数。 – Tronic 2010-01-17 04:45:41

+2

你确定吗?采用(char *,size_t)的字符串构造函数不会将char *参数视为c样式(以空字符结尾)的字符串。它使用你给它的长度。 – crmoore 2010-01-17 04:49:44

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和它的库下工作。但它在gcc上工作。 – 2010-01-17 05:50:55

+0

为什么。什么失败? – 2010-01-17 09:47:58

+0

看起来像MSVC的basic_streambuf :: pubsetbuf根本没有做任何事情。 http://stackoverflow.com/questions/1494182/setting-the-internal-buffer-used-by-a-standard-stream-pubsetbuf – 2010-01-17 11:44:44

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函数。 – 2012-05-16 15:52:00

相关问题