Как читать / писать структуру в двоичных файлах? - PullRequest
19 голосов
/ 01 апреля 2011

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

Также при получении предположим, что я знаю, как узнать размер вектора, как извлечь из двоичного файла переменную структуры, содержащую вектор всех сохраненных элементов?

Я могу сохранить что-то в двоичном файле (так как я вижу увеличение размера при записи), но когда я пытаюсь получить обратно элементы, я получаю размер вектора равным нулю.

К сожалению, я должен добиться этого, используя стандартный STL, и не использовать сторонние библиотеки.

Ответы [ 2 ]

28 голосов
/ 01 апреля 2011

Вы должны взглянуть на Boost Serialization .

Если вы не можете использовать сторонние библиотеки , вы должны знать, что C ++ не поддерживает сериализацию напрямую. Это означает, что вам придется сделать это самостоятельно.

В этой статье показан хороший способ сериализации пользовательского объекта на диск и его извлечения. И этот урок показывает вам, как начать прямо сейчас с fstream .

Это моя попытка:

РЕДАКТИРОВАТЬ : так как ОП спросил, как сохранить / извлечь больше, чем запись, я решил обновить исходный код.

Итак, что изменилось? Теперь есть массив student_t apprentice[3]; для хранения информации о 3 студентах. Весь массив сериализуется на диск, а затем все загружается обратно в ОЗУ, где возможно чтение / поиск определенных записей. Обратите внимание, что это очень очень маленький файл (84 байта). Я не предлагаю такой подход при поиске записей на огромных файлах.

#include <fstream>
#include <iostream>
#include <vector>
#include <string.h>

using namespace std;


typedef struct student
{
    char name[10];
    int age;
    vector<int> grades;
}student_t;

int main()
{
    student_t apprentice[3];  
    strcpy(apprentice[0].name, "john");
    apprentice[0].age = 21;
    apprentice[0].grades.push_back(1);
    apprentice[0].grades.push_back(3);
    apprentice[0].grades.push_back(5);    

    strcpy(apprentice[1].name, "jerry");
    apprentice[1].age = 22;
    apprentice[1].grades.push_back(2);
    apprentice[1].grades.push_back(4);
    apprentice[1].grades.push_back(6);

    strcpy(apprentice[2].name, "jimmy");
    apprentice[2].age = 23;
    apprentice[2].grades.push_back(8);
    apprentice[2].grades.push_back(9);
    apprentice[2].grades.push_back(10);

    // Serializing struct to student.data
    ofstream output_file("students.data", ios::binary);
    output_file.write((char*)&apprentice, sizeof(apprentice));
    output_file.close();

    // Reading from it
    ifstream input_file("students.data", ios::binary);
    student_t master[3];
    input_file.read((char*)&master, sizeof(master));         

    for (size_t idx = 0; idx < 3; idx++)
    {
        // If you wanted to search for specific records, 
        // you should do it here! if (idx == 2) ...

        cout << "Record #" << idx << endl;
        cout << "Name: " << master[idx].name << endl;
        cout << "Age: " << master[idx].age << endl;
        cout << "Grades: " << endl;
        for (size_t i = 0; i < master[idx].grades.size(); i++)
           cout << master[idx].grades[i] << " ";
        cout << endl << endl;
    }

    return 0;
}

Выходы

Record #0
Name: john
Age: 21
Grades: 
1 3 5 

Record #1
Name: jerry
Age: 22
Grades: 
2 4 6 

Record #2
Name: jimmy
Age: 23
Grades: 
8 9 10

Дамп двоичного файла :

$ hexdump -c students.data 
0000000   j   o   h   n  \0 237   {  \0   �   �   {   � 025  \0  \0  \0
0000010   (   �   �  \b   4   �   �  \b   8   �   �  \b   j   e   r   r
0000020   y  \0   �  \0   �   �   |  \0 026  \0  \0  \0   @   �   �  \b
0000030   L   �   �  \b   P   �   �  \b   j   i   m   m   y  \0  \0  \0
0000040   �   6   �  \0 027  \0  \0  \0   X   �   �  \b   d   �   �  \b
0000050   h   �   �  \b                                                
0000054
17 голосов
/ 01 апреля 2011

Обычно вы сериализуете вектор, записывая длину вектора с последующим количеством элементов. Когда вы читаете его обратно, сначала указывается длина, чтобы узнать, сколько еще элементов нужно прочитать как часть этого вектора. В качестве простого первого приближения рассмотрим что-то вроде этого:

template<class T>
std::ostream &operator<<(std::ostream &output, T const &input) {
    T::size_type size = input.size();

    output << size << "\n";
    std::copy(input.begin(), input.end(), 
         std::ostream_iterator<T::value_type>(output, "\n"));

    return output;
}

template<class T>
std::istream &operator>>(std::istream &input, T &output) {
    T::size_type size, i;

    input >> size;
    output.resize(size);
    std::copy_n(
        std::istream_iterator<t::value_type>(input),
        size,
        output.begin());

    return input;
}

Это открыто для множества твиков, улучшений и простых модификаций - например, на данный момент я передал вектор (или любой другой - может быть std :: deque и т. Д.) По ссылке вместо прохождения итераторов. Это, вероятно, упрощает большинство случаев использования, но не подходит для остальной библиотеки.

Это также сериализует в текстовом формате, один номер в строке. Обсуждения, сравнивающие текст с двоичным кодом, уже случались, поэтому я не буду пытаться повторить все аргументы здесь - просто отмечу, что та же самая базовая идея может быть реализована как в двоичном формате, так и в текстовом.

...