Считать блоки двоичного файлового буфера в разные типы

Я пытаюсь прочитать двоичный файл в память, а затем использовать его так:

struct myStruct {
std::string mystring; // is 40 bytes long
uint myint1; // is 4 bytes long
};

typedef unsigned char byte;

byte *filedata = ReadFile(filename); // reads file into memory, closes the file
myStruct aStruct;
aStruct.mystring = filedata.????

Мне нужен способ доступа к двоичному файлу со смещением и получения определенной длины при этом смещении.
Это легко, если я храню данные двоичного файла в std :: string, но я решил, что использовать это для хранения двоичных данных не так хорошо, как делать вещи. (filedata.substr(offset, len))

Разумно обширные поиски (IMO) ничего не нашли, есть идеи? Я готов изменить тип хранилища (например, на std :: vector), если вы считаете это необходимым.

1

Решение

Если вы не собираетесь использовать библиотеку сериализации, я предлагаю добавить поддержку сериализации для каждого класса:

struct My_Struct
{
std::string my_string;
unsigned int my_int;
void Load_From_Buffer(unsigned char const *& p_buffer)
{
my_string = std::string(p_buffer);
p_buffer += my_string.length() + 1; // +1 to account for the terminating nul character.
my_int = *((unsigned int *) p_buffer);
p_buffer += sizeof(my_int);
}
};

unsigned char * const buffer = ReadFile(filename);
unsigned char * p_buffer = buffer;
My_Struct my_variable;
my_variable.Load_From_Buffer(p_buffer);

Некоторые другие полезные методы интерфейса:

unsigned int Size_On_Stream(void) const; // Returns the size the object would occupy in the stream.
void Store_To_Buffer(unsigned char *& p_buffer); // Stores object to buffer, increments pointer.

С помощью шаблонов вы можете расширить функциональность сериализации:

void Load_From_Buffer(std::string& s, unsigned char *& p_buffer)
{
s = std::string((char *)p_buffer);
p_buffer += s.length() + 1;
}

void template<classtype T> Load_From_Buffer(T& object, unsigned char *& p_buffer)
{
object.Load_From_Buffer(p_buffer);
}

Изменить 1: причина не писать структуру напрямую

В C и C ++ размер структуры не может быть равен сумме размера ее членов.
Компиляторы разрешено вставлять набивка, или неиспользуемое пространство, между членами так, чтобы участники были выровнены по адресу.

Например, 32-разрядный процессор любит извлекать данные на границах 4 байта. Имея один char в структуре, сопровождаемой int сделает int по относительному адресу 1, который не кратен 4. Компилятор подушечка структура, так что int выравнивается по относительному адресу 4.

Структуры могут содержать указатели или элементы, которые содержат указатели.
Например, std::string Тип может иметь размер 40, хотя строка может содержать 3 символа или 300. Она имеет указатель на фактические данные.

Порядок байтов.
С многобайтовыми целыми числами некоторые процессоры, такие как старший значащий байт (MSB), сначала a.k.a. Big Endian (способ, которым люди читают числа) или сначала наименее значимый байт, a.k.a. Little Endian. Формат Little Endian требует меньше схем для чтения, чем Big Endian.

Редактировать 2: записи вариантов

При выводе таких вещей, как массивы и контейнеры, вы должны решить, хотите ли вы выводить полный контейнер (включая неиспользуемые слоты) или выводить только элементы в контейнере. Вывод только элементов в контейнере будет использовать вариант записи техника.

Два метода вывода вариантных записей: количество, за которым следуют позиции, или позиции, за которыми следует часовой. Последнее — это то, как пишутся строки в стиле C, а страж — нулевой символ.

Другой метод — вывести количество предметов, за которыми следуют предметы. Таким образом, если бы у меня было 6 чисел, 0, 1, 2, 3, 4, 5, результат был бы:
6 // Количество предметов
0
1
2
3
4
5

В приведенном выше методе Load_From_Buffer я бы создал временное хранилище для количества, выписал его, а затем следовал за каждым элементом из контейнера.

3

Другие решения

Вы можете перегрузить оператор вывода std :: ostream и оператор ввода std :: istream для своей структуры, примерно так:

struct Record {
std::string name;
int value;
};

std::istream& operator>>(std::istream& in, Record& record) {
char name[40] = { 0 };
int32_t value(0);
in.read(name, 40);
in.read(reinterpret_cast<char*>(&value), 4);
record.name.assign(name, 40);
record.value = value;
return in;
}

std::ostream& operator<<(std::ostream& out, const Record& record) {
std::string name(record.name);
name.resize(40, '\0');
out.write(name.c_str(), 40);
out.write(reinterpret_cast<const char*>(&record.value), 4);
return out;
}

int main(int argc, char **argv) {
const char* filename("records");
Record r[] = {{"zero", 0 }, {"one", 1 }, {"two", 2}};
int n(sizeof(r)/sizeof(r[0]));

std::ofstream out(filename, std::ios::binary);
for (int i = 0; i < n; ++i) {
out << r[i];
}
out.close();

std::ifstream in(filename, std::ios::binary);
std::vector<Record> rIn;
Record record;
while (in >> record) {
rIn.push_back(record);
}
for (std::vector<Record>::iterator i = rIn.begin(); i != rIn.end(); ++i){
std::cout << "name: " << i->name << ", value: " << i->value
<< std::endl;
}
return 0;
}
0