чтение и запись вектора структур в файл - PullRequest
1 голос
/ 29 ноября 2010

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

Вот суть моих методов чтения / записи.Предполагая vector<element> elements ...

Чтение:

ifstream infile;
infile.open("data.dat", ios::in | ios::binary);
infile.seekg (0, ios::end);
elements.resize(infile.tellg()/sizeof(element));
infile.seekg (0, ios::beg);
infile.read( (char *) &elements[0], elements.capacity()*sizeof(element));
infile.close();

Запись:

ofstream outfile;
outfile.open("data.dat", ios::out | ios::binary | ios_base::trunc);
elements.resize(elements.size());
outfile.write( (char *) &elements[0], elements.size() * sizeof(element));
outfile.close();

Элемент структуры:

struct element {
int id;
string test;
int other;        
};

Ответы [ 2 ]

6 голосов
/ 29 ноября 2010

В C ++ память, как правило, не может напрямую считываться и записываться на диск напрямую. В частности, ваш struct element содержит string, который не является POD типом данных, и поэтому не может быть доступен напрямую.

Мысленный эксперимент может помочь прояснить это. Ваш код предполагает, что все ваши element значения имеют одинаковый размер. Что произойдет, если одно из значений string test окажется длиннее, чем вы предполагали? Как ваш код узнает, какой размер использовать при чтении и записи на диск?

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

2 голосов
/ 29 ноября 2010

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

bool write_string(std::ostream& os, const std::string& s)
{
    size_t n = s.size();
    return os.write(n, sizeof n) && os.write(s.data(), n);
}

Затем вы можете написать подпрограммы сериализации для вашей структуры. Есть несколько вариантов дизайна: - многим нравится объявлять типы Binary_IStream / Binary_OStream, которые могут содержать std :: ostream, но будучи отдельным типом, можно использовать для создания отдельного набора подпрограмм сериализации ala:

operator<<(Binary_OStream& os, const Some_Class&);

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

Если вы сериализуете номера, вам необходимо решить, делать ли это в двоичном формате или в формате ASCII. В чисто двоичном формате, где требуется переносимость (даже между 32-битными и 64-битными компиляциями в одной и той же ОС), вам может потребоваться приложить некоторые усилия для кодирования и использования метаданных размера шрифта (например, int32_t или int64_t?) в качестве порядка байтов (например, рассмотрим порядок байтов в сети и ntohl () - функции семейства). С ASCII вы можете избежать некоторых из этих соображений, но это переменная длина и может быть медленнее для записи / чтения. Ниже я произвольно использую ASCII с '|' терминатор для цифр.

bool write_element(std::ostream& os, const element& e)
{
    return (os << e.id << '|') && write_string(os, e.test) && (os << e.other << '|');
}

А потом для вашего вектора:

os << elements.size() << '|';
for (std::vector<element>::const_iterator i = elements.begin();
     i != elements.end(); ++i)
    write_element(os, *i);

Чтобы прочитать это обратно:

std::vector<element> elements;
size_t n;
if (is >> n)
    for (int i = 0; i < n; ++i)
    {
        element e;
        if (!read_element(is, e))
            return false; // fail
        elements.push_back(e);
   }

... что нужно ...

bool read_element(std::istream& is, element& e)
{
    char c;
    return (is >> e.id >> c) && c == '|' &&
           read_string(is, e.test) &&
           (is >> e.other >> c) && c == '|';
}

... и ...

bool read_string(std::istream& is, std::string& s)
{
    size_t n;
    char c;
    if ((is >> n >> c) && c == '|')
    {
        s.resize(n);
        return is.read(s.data(), n);
    }
    return false;
}
...