2011-05-05 35 views
3

我有一個const std::vector<unsigned char>的形式的二進制數據塊,並希望能夠從中提取單個字段,例如4個字節的整數,1個布爾值等。這需要,儘可能可能的,既有效又簡單。例如。它應該能夠在不需要複製的情況下就地讀取數據(例如,寫入字符串或數組)。它應該能夠一次讀取一個字段,就像解析器一樣,因爲數據塊沒有固定的格式。我已經知道如何確定在每種情況下要讀取的字段類型 - 問題是在std::vector之上獲得可用的接口來完成此操作。從std :: vector讀取二進制數據的最簡單方法<unsigned char>?

但是我找不到一個簡單的方法將這些數據轉化爲一種易於使用的形式,它提供了有用的讀取功能。例如。 std::basic_istringstream<unsigned char>給了我一個閱讀界面,但似乎我需要先將數據複製到臨時std::basic_string<unsigned char>,這對於更大的數據塊來說並不是一個好主意。

也許有一些方法可以在這種情況下使用streambuf來讀取數據,但似乎我需要派生自己的streambuf類來做到這一點。

在我看來,我可能只是對向量的data()使用sscanf,並且這似乎比C++標準庫替代方案更簡潔和更有效。 編輯:有人提醒說,sscanf不會做我誤以爲它做了什麼,我其實不知道乾淨的方式來做這個在C或C++。但我錯過了什麼,如果是的話,什麼?

+0

您可以使用'std :: string'和'data()'方法。您可以使用您的按位運算符而無需複製任何內容。 – GWW 2011-05-05 18:24:28

+0

數據不會以std :: string的形式到達。我不確定你指的是哪個按位運算符 - 我需要按順序讀取字節。 – Kylotan 2011-05-05 18:26:13

+0

我很困惑你的衝突的要求。你說你有「一大堆二進制數據」,但你說你可以「可能只是使用sscanf」。 'scanf'讀取文本格式的數據,而不是二進制格式的數據。讓我這樣說:如果你的向量有一個int,它將被存儲爲一個32位的四字節2的補碼數組,或者被存儲爲幾個字符,每個字符的範圍是0-9? – 2011-05-05 19:28:43

回答

4

您可以訪問數據通過它的向量operator[]。保證向量的數據存儲在一個連續的數組中,並且[]返回對該數組成員的引用。您可以直接或通過memcpy使用該引用。

std::vector<unsigned char> v; 
... 
byteField = v[12]; 
memcpy(&intField, &v[13], sizeof intField); 
memcpy(charArray, &v[20], lengthOfCharArray); 

編輯1: 如果你想要的東西 「更方便」 是,你可以嘗試:

template <class T> 
ReadFromVector(T& t, std::size_t offset, 
    const std::vector<unsigned char>& v) { 
    memcpy(&t, &v[offset], sizeof(T)); 
} 

用法是:

std::vector<unsigned char> v; 
... 
char c; 
int i; 
uint64_t ull; 
ReadFromVector(c, 17, v); 
ReadFromVector(i, 99, v); 
ReadFromVector(ull, 43, v); 

編輯2:

struct Reader { 
    const std::vector<unsigned char>& v; 
    std::size_t offset; 
    Reader(const std::vector<unsigned char>& v) : v(v), offset() {} 
    template <class T> 
    Reader& operator>>(T&t) { 
    memcpy(&t, &v[offset], sizeof t); 
    offset += sizeof t; 
    return *this; 
    } 
    void operator+=(int i) { offset += i }; 
    char *getStringPointer() { return &v[offset]; } 
}; 

用法:

std::vector<unsigned char> v; 
Reader r(v); 
int i; uint64_t ull; 
r >> i >> ull; 
char *companyName = r.getStringPointer(); 
r += strlen(companyName); 
+0

我真的需要更方便的流式界面,因爲我需要從數據中讀取的字段不是一個簡單的固定格式。 – Kylotan 2011-05-05 19:29:18

+0

這些字段是否緊密排列,並且您保證按順序閱讀它們? – 2011-05-05 19:33:03

+0

是的,是的。看起來,編輯建議的一些變化可能是要走的路,但我很驚訝,似乎沒有簡單的方法將矢量作爲流或流緩衝的後備存儲。 – Kylotan 2011-05-05 19:37:37

1

您可以使用描述您嘗試提取的數據的結構。您可以從您的矢量數據移動到該結構是這樣的:

struct MyData { 
    int intVal; 
    bool boolVal; 
    char[15] stringVal; 
} __attribute__((__packed__)); 

// assuming all extracted types are prefixed with a one byte indicator. 
// Also assumes "vec" is your populated vector 
int pos = 0; 
while (pos < vec.size()-1) { 
    switch(vec[pos++]) { 
     case 0: { // handle int 
      int intValue; 
      memcpy(&vec[pos], &intValue, sizeof(int)); 
      pos += sizeof(int); 
      // do something with handled value 
      break; 
     } 
     case 1: { // handle double 
      double doubleValue; 
      memcpy(&vec[pos], &doubleValue, sizeof(double)); 
      pos += sizeof(double); 
      // do something with handled value 
      break; 
     } 
     case 2: { // handle MyData 
      struct MyData data; 
      memcpy(&vec[pos], &data, sizeof(struct MyData)); 
      pos += sizeof(struct MyData); 
      // do something with handled value 
      break; 
     } 
     default: { 
      // ERROR: unknown type indicator 
      break; 
     } 
    } 
} 
+0

請記住,二進制數據添加時必須以此格式存儲,並且您必須小心結構打包。我建議至少使用'stdint.h'中定義的固定寬度類型,或者使用編譯器標誌停止結構打包。 – Chad 2011-05-05 18:32:54

+0

不幸的是,這些數據是任意的,並且通常是可變長度的,所以這種方法對我來說不起作用。 – Kylotan 2011-05-05 18:33:31

+0

乍得是正確的,答案已被編輯,以解釋這 – 2011-05-05 18:59:11

1

如果您的向量存儲二進制數據,則不能使用sscanf的或類似的,他們對文字工作。 對於用於一個bool轉換一個字節是足夠

bool b = my_vec[10]; 

簡單對於提取一個儲存在大端順序一個unsigned int(假設你的整數是32位):

unsigned int i = my_vec[10] << 24 | my_vec[11] << 16 | my_vec[12] << 8 | my_vec[13]; 

A 16位無符號短將類似於:

unsigned short s = my_vec[10] << 8 | my_vec[11];¨ 
+1

對不起,也許我的原始問題並不清楚。我沒有提取單個字節的問題。但我需要的是一個更有用的流式界面,因爲我擁有任意數量的數據。我將編輯該問題更清楚。 – Kylotan 2011-05-05 18:41:12

+0

將上面的代碼打包在read_uint32(...),read_bool(...),read_blob()等函數中,提取各種元素並記住您當前的讀取位置。 – nos 2011-05-05 18:54:52

0

使用for循環遍歷向量並使用按位運算符來訪問每個位組。例如,訪問第一usigned字符的高4位的向量:

int myInt = vec[0] & 0xF0; 

要從右讀的第五位,該塊之後,我們剛讀:

bool myBool = vec[0] & 0x08; 

的三個最低顯著(最低)個位accesed像這樣:

int myInt2 = vec[0] & 0x07; 

然後,您可以重複這一過程(使用for循環)在你的向量的每一個元素。

+1

我沒有在我的問題中提到任何有關個別位的信息。 – Kylotan 2011-05-05 18:44:51

1

如果你能負擔Qt依賴,QByteArrayfromRawData()命名的構造函數,它包裝QByteArray中的現有數據緩衝區而不復制數據。使用該字節數組,您可以提供一個QTextStream

我不知道在標準流庫中的任何這樣的功能(短實現自己的streambuf,當然的),但我很樂意被證明是錯誤的:)

+0

不幸的是Qt不適合我,但我已經提高了這一點,因爲我期望這對其他人知道他們是否遇到了這個問題很有用。 – Kylotan 2011-05-05 19:40:29

相關問題