2011-05-05 78 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

相关问题