Заполнение структуры

Я пытаюсь прочитать фрагменты данных из файла непосредственно в структуру, но заполнение приводит к чтению слишком большого количества данных и смещению данных.

Должен ли я вручную читать каждую часть в структуре или есть более простой способ сделать это?

Мой код:

Структуры

typedef unsigned char byte;

struct Header
{
    char ID[10];
    int  version;
};

struct Vertex //cannot rearrange the order of the members
{
    byte    flags;
    float   vertex[3];
    char    bone;
    byte    referenceCount;
};

Как я читаю данные:

std::ifstream in(path.c_str(), std::ifstream::in | std::ifstream::binary);

Header header;
in.read((char*)&header.ID, sizeof(header.ID));
header.ID[9] = '\0';
in.read((char*)&header.version, sizeof(header.version));
std::cout << header.ID << " " << header.version << "\n";
in.read((char*)&NumVertices, sizeof(NumVertices));
std::cout << NumVertices << "\n";

std::vector<Vertex> Vertices(NumVertices);

for(std::vector<Vertex>::iterator it = Vertices.begin(); it != Vertices.end(); ++it)
{
    Vertex& v = (*it);
    in.read((char*)&v.flags, sizeof(v.flags));
    in.read((char*)&v.vertex, sizeof(v.vertex));
    in.read((char*)&v.bone, sizeof(v.bone));
    in.read((char*)&v.referenceCount, sizeof(v.referenceCount));
}

Я пытался сделать: in.read((char*)&Vertices[0], sizeof(Vertices[0]) * NumVertices);, но это дает неправильные результаты из-за того, что я считаю отступом.

Кроме того: на данный момент я использую приведения в стиле C, что будет правильным приведением C++ для использования в этом сценарии, или подходит ли приведение в стиле C?


person Rarge    schedule 27.04.2011    source источник
comment
Что касается последней части вашего вопроса, вы можете использовать reinterpret_cast‹char *›, что делает его очень явным.   -  person jv42    schedule 27.04.2011
comment
В этом есть нечто большее, чем я думал :P   -  person Rarge    schedule 27.04.2011


Ответы (6)


Если вы записываете всю структуру в двоичном виде, вам не нужно читать ее, как если бы вы сохраняли каждую переменную отдельно. Вы просто прочитаете размер структуры из файла в структуру, которую вы определили.

Header header;
in.read((char*)&header, sizeof(Header));

Если вы всегда используете одну и ту же архитектуру или один и тот же компьютер, вам не нужно беспокоиться о проблемах с порядком байтов, поскольку вы будете записывать их так же, как ваше приложение должно их считывать. Если вы создаете файл на одной архитектуре и ожидать, что он будет переносимым/пригодным для использования на другой, тогда вам нужно будет соответствующим образом поменять местами байты. Раньше я делал это, создавая собственный метод подкачки. (например, Swap.h)

Swap.h - This is the header you use within you're code

void swap(unsigned char *x, int size);

------------------

SwapIntel.cpp - This is what you would compile and link against when building for Intel

void swap(unsigned char *x, int size)
{
    return;   // Do nothing assuming this is the format the file was written for Intel (little-endian)
}

------------------

SwapSolaris.cpp -  This is what you would compile and link against when building for Solaris

void swap(unsigned char *x, int size)
{
    // Byte swapping code here to switch from little-endian to big-endian as the file was written on Intel
    // and this file will be the implementation used within the Solaris build of your product
    return;   
}
person RC.    schedule 27.04.2011
comment
Когда вы записываете структуру в файл, она будет записана со всеми данными, включая окончания нулевой строки и отступы. Поэтому, когда вы читаете его обратно, все возвращается на свои места, как и ожидалось. - person RC.; 27.04.2011
comment
О, я понимаю (я думаю). Файлы не обязательно были записаны на той же машине, которая их читает, поэтому файлы также могут иметь разный порядок следования байтов, и поэтому также возникают проблемы с выравниванием? - person Rarge; 27.04.2011
comment
Да, вам нужно убедиться, что вы не записываете данные так же, как читаете их в настоящее время. (т. е. у вас не должно быть такого кода, как write((char*)&header.ID, 10)) Вы должны писать структуру целиком, как в write((char*)&header, sizeof(header)); затем прочитайте его, как указано выше. - person RC.; 27.04.2011
comment
Великолепно, я не знал, что это тоже связано со структурой файла. Спасибо. У меня есть вопрос о порядке байтов; Будет ли вся структура находиться в файле в обратном порядке или элементы останутся в том же порядке, но их данные будут в обратном порядке? - person Rarge; 27.04.2011

Нет, вам не нужно читать каждое поле отдельно. Это называется выравниванием/упаковкой. См. http://en.wikipedia.org/wiki/Data_structure_alignment.

Приведение в стиле C эквивалентно reinterpret_cast. В этом случае вы используете его правильно. Вы можете использовать синтаксис, специфичный для C++, но это требует гораздо большего набора текста.

person Community    schedule 27.04.2011
comment
но это намного больше набора текста. это очень печальный момент, с сегодняшним инструментарием. - person jv42; 27.04.2011
comment
@ jv42: Имея два синтаксически разных варианта описания одного и того же выражения, я предпочитаю самый короткий, даже если редактор выполняет автозаполнение. - person ; 27.04.2011
comment
Это ваш выбор, и это нормально, я часто использую приведения в стиле C при написании кода на C++. Но выразительность приведений в стиле C++ намного лучше и передает намерения без добавления комментариев (т. е. я нарушаю спецификатор const, выполняю ли я преобразование типа или возился с указателями). - person jv42; 27.04.2011

Вы можете изменить заполнение, явно попросив ваш компилятор выровнять структуры по 1 байту вместо 4 или как там по умолчанию. В зависимости от среды это можно сделать разными способами, иногда файл за файлом («модуль компиляции») или даже структуру за структурой (с прагмами и т. д.) или только для всего проекта.

person jv42    schedule 27.04.2011

header.ID[10] = '\0';

header.ID[9] — последний элемент массива.

person Oswald    schedule 27.04.2011
comment
Ой! В последнее время слишком много использовал Lua: P - person Rarge; 27.04.2011

Если вы используете компилятор Microsoft, изучите прагму align. Также есть включаемые файлы выравнивания:

#include <pshpack1.h>
// your code here
#include <poppack.h>

GNU gcc имеет другую систему, которая позволяет вам добавлять выравнивание/дополнение к определению структуры.

person J Evans    schedule 27.04.2011

Если вы читаете и записываете этот файл самостоятельно, попробуйте библиотеку Google Protobuf. Он будет обрабатывать все вопросы порядка байтов, выравнивания, заполнения и языкового взаимодействия.

http://code.google.com/p/protobuf/

person blaze    schedule 27.04.2011