#c #struct #padding
#c #структура #заполнение
Вопрос:
Я пытаюсь считывать фрагменты данных из файла непосредственно в структуру, но заполнение приводит к считыванию слишком большого количества данных и их смещению.
Должен ли я вручную считывать каждую часть в структуру или есть более простой способ сделать это?
Мой код:
Структуры
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*)amp;header.ID, sizeof(header.ID));
header.ID[9] = '';
in.read((char*)amp;header.version, sizeof(header.version));
std::cout << header.ID << " " << header.version << "n";
in.read((char*)amp;NumVertices, sizeof(NumVertices));
std::cout << NumVertices << "n";
std::vector<Vertex> Vertices(NumVertices);
for(std::vector<Vertex>::iterator it = Vertices.begin(); it != Vertices.end(); it)
{
Vertexamp; v = (*it);
in.read((char*)amp;v.flags, sizeof(v.flags));
in.read((char*)amp;v.vertex, sizeof(v.vertex));
in.read((char*)amp;v.bone, sizeof(v.bone));
in.read((char*)amp;v.referenceCount, sizeof(v.referenceCount));
}
Я пытался выполнить: in.read((char*)amp;Vertices[0], sizeof(Vertices[0]) * NumVertices);
но это приводит к неверным результатам из-за того, что я считаю дополнением.
Также: на данный момент я использую приведения в стиле C, какое приведение C было бы правильным для использования в этом сценарии или приведение в стиле C подойдет?
Комментарии:
1. Что касается последней части вашего вопроса, вы могли бы использовать reinterpret_cast<char *> , что делает его очень явным.
2. В этом есть нечто большее, чем я сначала подумал: P
Ответ №1:
Если вы записываете всю структуру в двоичном формате, вам не нужно читать ее так, как если бы вы сохраняли каждую переменную отдельно. Вы бы просто прочитали размер структуры из файла в структуру, которую вы определили.
Header header;
in.read((char*)amp;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;
}
Комментарии:
1. Когда вы записываете структуру в файл, она будет записана со всеми данными, включая ваши нулевые окончания строк и отступы. Поэтому, когда вы читаете это снова, все возвращается на свои места так, как и ожидалось.
2. О, я понимаю (я думаю). Файлы не обязательно были написаны на том же компьютере, который их читает, поэтому файлы также могут иметь разный порядковый номер, и поэтому также возникают проблемы с выравниванием?
3. Да, вам нужно убедиться, что вы не записываете данные так же, как вы их читаете в данный момент. (т. Е. у вас не должно быть кода типа write((char *)amp;header. ИДЕНТИФИКАТОР, 10) ) Вы должны записывать структуру целиком, как в write((char*)amp;header, sizeof(заголовок)); затем прочитайте это, как указано выше.
4. Блестяще, я не знал, что это также связано с тем, как структурирован файл. Спасибо. У меня вопрос о порядке следования; Будет ли вся структура в файле «в обратном порядке» или элементы по-прежнему будут располагаться в том же порядке, но их данные будут в обратном порядке?
Ответ №2:
Нет, вам не обязательно читать каждое поле отдельно. Это называется выравниванием / упаковкой. Смотрите http://en.wikipedia.org/wiki/Data_structure_alignment
Приведение в стиле C эквивалентно reinterpret_cast
. В этом случае вы используете его правильно. Вы можете использовать синтаксис, специфичный для C , но это требует гораздо больше ввода.
Комментарии:
1. «но это намного больше ввода». это очень печальный момент с сегодняшним инструментарием.
2. @jv42: Имея два синтаксически разных варианта описания одного и того же выражения, я предпочитаю самый короткий, даже если редактор автоматически завершается.
3. Это ваш выбор, и это нормально, я сам часто использую приведения в стиле C при написании кода на C . Но выразительность приведений в стиле C намного лучше и передает намерения без добавления комментариев (т. Е. Нарушаю ли я спецификатор const, выполняю ли я преобразование типов или я путаюсь с указателями).
Ответ №3:
Вы можете изменить заполнение, явно попросив ваш компилятор выровнять структуры по 1 байту вместо 4 или как там по умолчанию. В зависимости от среды, это может быть сделано многими различными способами, иногда файл за файлом (‘единица компиляции’) или даже структура за структурой (с прагмами и тому подобным) Или только для всего проекта.
Ответ №4:
header.ID[10] = '';
header.ID [9] является последним элементом массива.
Комментарии:
1. Упс! В последнее время слишком часто использую Lua: P
Ответ №5:
Если вы используете компилятор Microsoft, тогда изучите прагму align. Существуют также файлы, включающие выравнивание:
#include <pshpack1.h>
// your code here
#include <poppack.h>
GNU gcc имеет другую систему, которая позволяет добавлять выравнивание / дополнение к определению структуры.
Комментарии:
1. Компилятор Microsoft также поддерживает пакетные прагмы. Я предпочитаю использовать их, поскольку код становится более переносимым / независимым от компилятора. Смотрите msdn.microsoft.com/en-us/library/ms253935.aspx
2. При компиляции, если программа выполняется на другом компьютере, будут ли структуры по-прежнему иметь заполнение, заданное любым из приведенных здесь методов? (Я чувствую, что это может быть глупый вопрос)
3. @Rarge — да, заполнение влияет на двоичное изображение, поэтому исправляется во время компиляции / компоновки.
Ответ №6:
Если вы читаете и записываете этот файл самостоятельно, попробуйте библиотеку Google Protobuf. Он будет обрабатывать все вопросы с порядком следования, выравниванием, заполнением и языковым взаимодействием.