Простейший способ чтения двоичных данных из std :: vector? - PullRequest
3 голосов
/ 05 мая 2011

У меня есть набор двоичных данных в форме const std::vector<unsigned char>, и я хочу иметь возможность извлекать из него отдельные поля, такие как 4 байта для целого числа, 1 для логического значения и т. Д. Это должно быть,насколько это возможно, как эффективно, так и просто.например.Он должен иметь возможность читать данные на месте без необходимости их копирования (например, в строку или массив).И он должен иметь возможность читать по одному полю за раз, как анализатор, поскольку куча данных не имеет фиксированного формата.Я уже знаю, как определить, какой тип поля для чтения в каждом случае - проблема заключается в получении полезного интерфейса поверх std::vector для этого.

Однако я не могу найти простой способполучить эти данные в удобной форме, которая дает мне полезные функции чтения.например.std::basic_istringstream<unsigned char> дает мне интерфейс для чтения, но мне кажется, что сначала мне нужно скопировать данные во временную std::basic_string<unsigned char>, что не подходит для больших блоков данных.

Может быть, есть какой-то способ, которым я могув этом случае используйте streambuf для чтения данных на месте, но для этого мне нужно было бы получить собственный класс streambuf. * ​​1009 *

Мне кажется, что я могу простоиспользуйте sscanf для данных вектора (), и это может показаться более кратким и более эффективным, чем альтернативы стандартной библиотеке C ++. РЕДАКТИРОВАТЬ: напомнив, что sscanf не делает то, что я ошибочно думал,на самом деле не знаю чистый способ сделать это на C или C ++.Но я что-то упустил, и если да, то что?

Ответы [ 5 ]

4 голосов
/ 05 мая 2011

У вас есть доступ к данным в векторе через operator[].Данные вектора гарантированно хранятся в одном непрерывном массиве, и [] возвращает ссылку на член этого массива.Вы можете использовать эту ссылку напрямую или через memcpy.

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

РЕДАКТИРОВАТЬ 1: Если вы хотите что-то «более удобное», вы можете попробовать:быть:

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 голос
/ 05 мая 2011

Если вы можете позволить себе зависимость Qt, QByteArray имеет именованный конструктор fromRawData () , который упаковывает существующие буферы данных в QByteArray без копирования данных.С этим байтовым массивом вы можете подавать QTextStream.

Я не знаю ни одной такой функции в стандартной библиотеке потоков (если не считать реализации вашей собственной streambuf,конечно), но я бы с радостью оказался неправ :))

1 голос
/ 05 мая 2011

Если ваш вектор хранит двоичные данные, вы не можете использовать sscanf или аналогичные, они работают с текстом. Для преобразования байта в bool достаточно просто

bool b = my_vec[10];

Для извлечения целого без знака, хранящегося в порядке с прямым порядком байтов (при условии, что ваши целые числа 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];¨
1 голос
/ 05 мая 2011

Вы можете использовать структуру, которая описывает данные, которые вы пытаетесь извлечь.Вы можете переместить данные из вашего вектора в структуру следующим образом:

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 голосов
/ 05 мая 2011

Используйте цикл for для итерации по вектору и используйте побитовые операторы для доступа к каждой группе битов. Например, чтобы получить доступ к старшим четырем битам первого назначенного символа в вашем векторе:

int myInt = vec[0] & 0xF0;

Чтобы прочитать пятый бит справа, сразу после фрагмента мы только что прочитали:

bool myBool = vec[0] & 0x08;

Три младших (младших) бита могут быть приняты следующим образом:

int myInt2 = vec[0] & 0x07;

Затем вы можете повторить этот процесс (используя цикл for) для каждого элемента в вашем векторе.

...