Как сохранить структуры в файле, чтобы легко перезагрузить? - PullRequest
2 голосов
/ 21 декабря 2010

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

struct object
{
    unsigned short id;
    float x;
    float y;
    unsigned short type;
    unsigned char flags;
};

struct sector
{
    unsigned long id;
    unsigned long neighbors[4]; // North,East,South,West
    std::vector<object> objects;
};

Я попробовал приведенный ниже код, чтобы записать структуру в файл, но она работает не очень хорошо.Размер файла после выполнения составляет всего 4,7 КБ, хотя он должен быть намного больше.

sector s;
object o;
s.id = 1;
s.neighbors = {2, 3, 4, 5};
o.id = 1; o.x = 2.0f; o.y = 3.0f; o.type = 4; o.flags = 6;
for(int i = 0; i < 65536; i++)
{
    s.objects.push_back(o);
    o.id += 1; o.x += 1.0f; o.y += 1.0f; o.type += 1; o.flags += 1;
}

ofstream os("world.dat", ios::binary|ios::out);
s.objects.resize(s.objects.size());
int size = s.objects.size() * sizeof(object);
os.write((char*)&size, sizeof(int));
os.write((char*)&s, size);
os.close();

Есть предложения?

Ответы [ 6 ]

4 голосов
/ 22 декабря 2010

Похоже, вы могли бы использовать библиотеку сериализации, такую ​​как boost :: serialization . Это поддерживает контейнеры STL из коробки, и поэтому очень просто поддерживать структуры в вашем примере.

3 голосов
/ 21 декабря 2010

Вы не можете написать структуру, содержащую вектор, используя необработанный байтовый ввод-вывод. Класс vector представляет собой небольшую структуру данных, которая хранит свои данные где-то в куче, а не в структуре. Это потому, что структура не может заранее «знать», сколько объектов она будет содержать.

Вы можете записать содержимое вектора так:

os.write((const char*)&s.id, sizeof(s.id));
os.write((const char*)&s.neighbors, sizeof(s.neighbors));
os.write((const char*)&s.objects[0], size);

Относительно того, почему файл занимает всего 4,7 КБ, я не знаю. Распечатайте size, чтобы увидеть, сколько данных записывается. Я проверил код после вышеуказанных изменений, и он работает.

Кроме того, вам не нужно s.objects.resize(s.objects.size());. Изменение размера вектора до его собственного размера является излишним.

Также обратите внимание на небольшую причуду. sizeof (object) == 16 в моей архитектуре, что больше, чем требуется для вышеуказанной структуры (2 + 4 + 4 + 2 + 1 == 13). Компилятор обычно добавляет заполнение, чтобы гарантировать, что примитивы размером 2 n выровнены по границе 2 n , что приводит к отверстия внутри каждой структуры и между структурами в векторе.

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

0 голосов
/ 22 декабря 2010

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

0 голосов
/ 22 декабря 2010

Если вы хотите знать, почему в файле хранится только 4.7K, вам нужно будет указать, какую ОС вы используете и какой компилятор C ++. Так как 's' находится в стеке, было бы разумным предположением, что только 4,7K стека использовалось, когда вы решили записать '& s' в файл. В Windows блок среды занимает 3,8 КБ стекового пространства. Поэтому в Windows ваше приложение использовало дополнительные 0,9 КБ стекового пространства (после блока среды) в точке, в которой вы решили вызвать write () для '& s', и поскольку стек растет вниз, реализация C ++ выбрала только выполнять (молча) запись () до точки, где начинается стек, т.е. на 4,7 К выше '& s'.

0 голосов
/ 22 декабря 2010

Вы смотрели на fread() и fwrite()?Если вам не нужна удобочитаемость файла, а объект и сектор - это POD, тогда вам подойдет такой тип:

object o;
FILE * fh = ...;
...
fwrite( &o, sizeof(o), 1, fh );

fread( &o, sizeof(o), 1, fh );

Обычно я бы не рекомендовал этот тип Cориентированное кодирование, но иногда старые способы все еще самые простые.

Обратите внимание, что этот код требует, чтобы массив секторов в объекте имел постоянный размер.

0 голосов
/ 22 декабря 2010

Я думаю, я только что нашел решение. Количество объектов является постоянным, поэтому использование вектора не требуется. Я заменил vector<object> objects на object objects[256][256], который правильно записан в файл с моим текущим кодом. Я также отказался от отслеживания положения одного сектора относительно другого по идентификаторам и соседним идентификаторам в пользу системы координат x, y. Должно быть легче отслеживать. Хотелось бы знать, почему я всегда усложняю вещи, а потом упрощаю их.

struct object
{
    unsigned short id;
    float x;
    float y;
    unsigned short type;
    unsigned char flags;
};
struct sector
{
    unsigned long x;
    unsigned long y;
    object objects[256][256];
};
sector s;
object o;

s.x = 1; s.y = 1;
o.id = 1; o.x = 2.0f; o.y = 3.0f; o.type = 4; o.flags = 6;
for(int x = 0; x < 256; x++)
{
    for(int y = 0; y < 256; y++)
    {
        s.objects[x][y] = o;
        o.id += 1; o.x += 1.0f; o.y += 1.0f; o.type += 1; o.flags += 1;
    }
}

ofstream os("world.dat", ios::binary|ios::out);
int size = sizeof(sector);
os.write((char*)&s, size);
os.close();

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...