如何读取/写入二进制文件中的结构?

发布于 2024-10-29 00:34:35 字数 269 浏览 2 评论 0原文

我面临一个小问题。我有一个结构,它有一个向量。请注意,每次迭代向量都是动态的。现在,在特定的迭代中,如何将包含大小为 n 的向量的结构存储到二进制文件中?

另外,在检索时,假设我知道向量的大小,如何从二进制文件中检索包含所有存储元素的向量的结构变量?

我能够将一些内容存储到二进制文件中(因为我可以看到写入时大小增加),但是当我尝试检索元素时,我得到的向量大小为零。

不幸的是,我必须使用标准 STL 来实现这一点,而不是使用任何第三方库。

I am facing a small problem. I have a struct, which has a vector. Note that the vector is dynamic per every iteration. Now, in a particular iteration, how do I store the struct which contains a vector of size n to a binary file?

Also, when retrieving, assume that I know how the size of the vector, how to I retrieve from the binary file, the struct variable containing the vector of all the stored elements?

I am able to store something to the binary file (as I can see the size increasing when writing), but when I am trying to retrieve back the elements, I am getting size of vector to be zero.

Unfortunately, I have to achieve this using the standard STL and not use any third-party libraries.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绾颜 2024-11-05 00:34:35

您应该查看Boost Serialization

如果你不能使用第三方库,你必须知道C++不直接支持序列化。这意味着您必须自己做。

本文展示了将自定义对象序列化到磁盘并将其检索回来的好方法。 本教程向您展示如何立即开始使用 fstream< /em>.

这是我的尝试:

编辑:由于OP询问如何存储/检索比记录更多的内容,我决定更新原始代码。

那么,什么改变了呢?现在有一个数组 student_t apprentice[3];来存储3个学生的信息。整个数组被序列化到磁盘,然后全部加载回 RAM,在 RAM 中可以读取/搜索特定记录。请注意,这是一个非常非常小的文件(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

You should have a look at Boost Serialization.

If you can't use 3rd party libraries, you must know that C++ doesn't support serialization directly. This means you'll have to do it yourself.

This article shows a nice way of serializing a custom object to the disk and retrieving it back. And this tutorial shows you how to get started right now with fstream.

This is my attempt:

EDIT: since the OP asked how to store/retrieve more than record I decided to updated the original code.

So, what changed? Now there's an array student_t apprentice[3]; to store information of 3 students. The entire array is serialized to the disk and then it's all loaded back to the RAM where reading/searching for specific records is possible. Note that this is a very very small file (84 bytes). I do not suggest this approach when searching records on huge files.

#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;
}

Outputs:

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

Dump of the binary file:

$ 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
雪化雨蝶 2024-11-05 00:34:35

通常,您可以通过写入向量的长度,后跟元素数量来序列化向量。当您读回它时,首先考虑长度可以让您知道作为该向量的一部分还需要读取多少项。作为一个简单的第一个近似,考虑这样的事情:

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 等)通过引用而不是传递迭代器。这可能简化了大多数使用,但与库的其余部分不太适合。

这也以文本格式序列化,每行一个数字。将文本与二进制进行比较的讨论之前已经发生过,所以我不会尝试在这里重复所有的论点——我只是指出,相同的基本想法可以以二进制格式和文本一样完成。

You typically serialize a vector by writing the length of the vector, followed by that number of elements. When you read it back in, having the length come first lets you know how many more items to read as part of that vector. As a simple first approximation, consider something like this:

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;
}

This is open to lots of tweaks, improvements, and simple modifications -- just for example, for the moment, I've passed the vector (or whatever -- could be a std::deque, etc.) by reference rather than passing iterators. That probably simplifies most use, but doesn't fit as well with the rest of the library.

This also serializes in text format, one number per line. Discussions comparing text to binary have happened before, so I won't try to repeat all the arguments here -- I'll just note that the same basic idea can be done in binary format just as well as text.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文