Saving in binary files

So what I'm trying to do is to save a linked list, like the one bellow, in a binary file so in case that the program crashes or something, I don't have to re-add all the nodes that I previously added, the thing is I don't have the slightest clue on how to do this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <string>

using namespace std;

class product_node
{
public:
    string name;
    string brand;
    unsigned int productCode;
    unsigned int typeCode;
    double price;
    unsigned int stock;
    unsigned int minStock;
    unsigned int maxStock;


    product_node* next;
};

class product_list
{


public:
    product_node* head;
    product_list()
    {
        head=NULL;
    }
    ~product_list()
    {
            no_produtos *current, *next;
            atual=head;
            while(current!=NULL)
            {
                next=current->next;
                delete current;
                current=next;
            }
    }

    void addProduct(unsigned int productCode, string name, string brand, 
    unsigned int typeCode, double price, unsigned int minStock,unsigned int 
    maxStock,unsigned int stock);
    
    void writeList();
   
};
Assuming you have a list where you could iterate over, then your code for writing to a binary file could look like:
1
2
3
4
5
    std::ofstream ofs("list.bin");
    for ( auto & item : my_list )
    {
        ofs.write( (char*)&item, sizeof(item) );
    }


see https://en.cppreference.com/w/cpp/io/basic_ostream/write
How can i apply hero to my list, do i have to do one of those loops for each variable in the node? And im guessing that id also need to do it for each node in the list.
how about reading from the file?
then your code for writing to a binary file could look like

Not in this case because of those two C++ strings in the node class.

Also you should start using C++ style casts, they are safer (if one can say any cast is safe).

how about reading from the file?

If you must use binary files, I recommend you start by getting the "writing" correct. But if binary files are not a "must use" you should really consider sticking with normal text files.


unfortunately binary files are required haha, i also dont know how to do the writing
should it be something like this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Node* current = head;
FILE* fp = fopen("save.bin", "wb");

while (current != NULL) {
    fwrite(&current->code, sizeof(int), 1, fp);
    fwrite(&current->price, sizeof(double), 1, fp);

    // The string (plus the null byte)
    fwrite(current->name.c_str(), sizeof(char), current->name.length() + 1, fp);
    // newline to indicate new node
    if (current->next != NULL)
        fprintf(fp, "\n");

    current = current->next;
}

fclose(fp);
The trick is to create read() and write() methods for the little things, and build up to the big things.

Always write the read() and write() methods as a pair. Whatever is written by write() should be readable by read():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Read and write a string
bool readString(istream &is, string &str)
{
    // a string is the size followed by the data
    size_t sz;
    is.read(reinterpret_cast<char*>(&sz), sizeof(sz));
    str.resize(sz);
    is.read(const_cast<char*>(str.data()), sz); // might not be portable
    return is.good();
}

bool writeString(ostream &os, string &str)
{
    // a string is the size followed by the data
    size_t sz = str.size();
    os.write(reinterpret_cast<char*>(&sz), sizeof(sz));
    os.write(const_cast<char*>(str.data()), sz); // might not be portable
    return os.good();
}

// Read and write a node. This does NOT change the next pointer
bool product_node::read(istream &is)
{
    readString(is, name);
    readString(is, brand);
    is.read(reinterpret_cast<char*>(&productCode), sizeof(productCode));
    // ADD CODE TO READ THE OTHER MEMBERS
    return is.good();
}

bool product_node::write(ostream &os)
{
    writeString(os, name);
    writeString(os, brand);
    os.write(reinterpret_cast<char*>(&productCode), sizeof(productCode));
    // ADD CODE TO WRITE THE OTHER MEMBERS
    return os.good();
}


// Read and write a list. Each node is written as a bool (true), followed'
// by the record. When bool is false, that indicates the end of the list.
bool product_list:read(istream &is)
{
    // ADD CODE TO READ THE LIST. Use product_node::read() to read
    // individual records
    return is.good();
}

bool product_list::write(ostream &os)
{
    // ADD CODE TO READ THE LIST. Use product_node::write() to write
    // individual records
    return os.good();
}


But remember if your class has non-trivial constructed member variables you must write out each member variable one at a time. For example you can't directly write a std::string, you must first write the "length" of the string, then write the std::string.c_str() of that variable. Remember that a std::string is not a fixed length, and the data is "hidden" in basically as a pointer. So if you just write the std::string you actually write out a pointer, not the actual data.

josilva00, could you tell why it's not allowed to do reading and writing in text mode? That makes no sense to me, at least not for your data that you have to store.
you can't directly write a std::string, you must first write the "length" of the string, then write the std::string.c_str() of that variable.

I got bit by that when I was converting an older "save C string data" bit of code to "save std::string" by just writing the std::string to the file. It worked to save and retrieve some test data, until I changed the data. *CRASH, BANG, BOOM* Ooops!
nuderobmonkey, this is part of a school project that I'm working on, Its already done, but I can get extra points if I manage to save in binary files
dhayden, i think i've managed to write to the file, here is the code
1
2
3
4
5
6
7
8
9
10
product_list::write(ostream &os)
{
    product_node*current=head;
    while(current!=NULL)
    {
        current->write(os,current->name,current->brand,current->productCode,current->typeCode,current->price,current->stock,current->minStock,current->maxStock);
        current=current->next;
    }
    return os.good();
}


How do i read and fill the the list?


Edit:i opened the binary file after writing to it and its empty
Last edited on
i opened the binary file after writing to it and its empty
Did you close the output file before opening the input file? Did product_list::write() return true?

How do i read and fill the the list?
You can't with that write() code because there's no way to tell when you hit the end of the list. That's why my read() method had this comment:
1
2
// Read and write a list. Each node is written as a bool (true), followed
// by the record. When bool is false, that indicates the end of the list. 


You need to write the bool. That way when you're reading the list, you know when to stop.

An alternative is to make one pass through the list, counting the items. They write the count, followed by all the items (without the bool). When reading , you read the count, and then read exactly that many items.

Oh, and a bonus: when reading the list, you should clear any contents in it first.
i opened the binaryfile.bin, its size is 0kb and its empty after i use the write method
I've managed to make it work, to write and read back and add the nodes, just one question, how do i clear a binary file before writing to it?
Just open if for writing. That will clear it automatically.
Topic archived. No new replies allowed.