2010-01-17 28 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

相關問題