从std :: vector <unsigned char =“”>?</unsigned>读取二进制数据的最简单方法

时间:2011-05-05 18:18:31

标签: c++ serialization iostream

我有const std::vector<unsigned char>形式的二进制数据块,并希望能够从中提取单个字段,例如4个字节表示整数,1表示布尔值等。这需要尽可能高效和简单。例如。它应该能够在不需要复制数据的情况下读取数据(例如,将其复制到字符串或数组中)。它应该能够一次读取一个字段,就像解析器一样,因为数据块没有固定的格式。我已经知道如何确定在每种情况下要读取的字段类型 - 问题是在std::vector之上获得一个可用的接口来执行此操作。

但是我找不到一种简单的方法将这些数据转换成一个易于使用的形式,它给了我有用的读取功能。例如。 std::basic_istringstream<unsigned char>给了我一个阅读界面,但似乎我需要先将数据复制到一个临时的std::basic_string<unsigned char>,这对于更大的数据块是不可能的。

也许有一些方法可以在这种情况下使用streambuf来读取数据,但看起来我需要派生自己的streambuf类才能做到这一点。

在我看来,我可能只是在向量的数据()上使用sscanf,这似乎比C ++标准库替代品更简洁,更有效。编辑:拥有有人提醒过,sscanf没有做我错误认为的事情,我实际上不知道用C或C ++做这件事的干净方法。但是我错过了什么,如果是的话,是什么?

5 个答案:

答案 0 :(得分: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);

答案 1 :(得分: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;
        }
    }
}

答案 2 :(得分:1)

如果你的矢量存储二进制数据,你不能使用sscanf或类似的,它们处理文本。 为bool转换一个字节很简单

bool b = my_vec[10];

用于提取以big endian顺序存储的unsigned int(假设你的int是32位):

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

16位无符号短路类似:

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

答案 3 :(得分:1)

如果能够负担Qt依赖关系,QByteArray具有fromRawData()命名构造函数,它将现有数据缓冲区包装在QByteArray中而不复制数据。使用该字节数组,您可以输入QTextStream

我不知道标准流库中有任何这样的功能(当然没有实现你自己的streambuf),但我很想被证明是错误的:)

答案 4 :(得分:0)

使用for循环迭代向量并使用按位运算符访问每个位组。例如,要访问向量中第一个usigned char的高四位:

int myInt = vec[0] & 0xF0;

要读取右边的第五位,就在我们刚看到的块之后:

bool myBool = vec[0] & 0x08;

可以像这样接收三个最低有效(最低)位:

int myInt2 = vec[0] & 0x07;

然后,您可以对向量中的每个元素重复此过程(使用for循环)。