0

I want to be able to write a linked list of type Music into a binary file, then later, read it back into a linked list. Of course, I'm having problems doing so. From what I understand, my problem is that string is a variable length. Since a string isn't always the same size, when reading it, there's no telling how much to read. I was going to convert my class to fixed-length cstrings to fix the problem. Actually, I tried to, but had a few other problems that were caused by that. My question is, is there any way to do achieve what I want without drastically altering my code?

void ReadBinFile(Node * head, string filename)
{
    Music new_song;
    ifstream bin(filename, ios::in | ios::binary);
    if (bin.is_open())
    {
        while (!bin.eof())
        {
            bin.read(reinterpret_cast<char *>(&new_song), sizeof(Music));
            if (!bin.eof())
            {
                Node * new_node = CreateNode(new_song);
                AppendNode(head, new_node);
            }
        }
        bin.close();
    }
}
void Save(Node * head, string filename)
{
    ofstream bin(filename, ios::out | ios::binary);
    if (bin.is_open())
    {
        Node * traveler = head;
        while (traveler != nullptr)
        {
            bin.write(reinterpret_cast<char *>(&(traveler->song)), sizeof(Music));
            traveler = traveler->next;
        }
        bin.close();
    }
}
Node * CreateNode(Music song)
{
    Node * new_node = new Node;
    new_node->song = song;
    new_node->next = nullptr;

    return new_node;
}
//music.h
class Music
{
    public:
        Music(string name = "", string artist = "");

    private:
        string m_name;
        string m_artist;
};
3
  • care to add info about how the the format of your binary file looks like? Commented Jun 3, 2015 at 6:45
  • @CyberSpock I would, but I don't exactly know what the format is. I don't know a whole lot about binary files. I could copy+paste an example of one generated, if that would help. Commented Jun 3, 2015 at 6:52
  • you need to first figure out the format, then after that create the pack/unpack functions to read/write one record. Commented Jun 3, 2015 at 7:04

2 Answers 2

1

Unfortunately You use strings, which are classes on their own (with dynamically assigned length). If You want to write a whole class, it'd have to be all-constant-size, but even then I wouldn't recommend it.

Add Read and Write methods to the class and read/write each field on it's own.


As a clarification:
You want to change Music like that:
class Music { public: Music(string name = "", string artist = ""); void Write(std::string filename); void Read(str::string filename); private: string m_name; string m_artist; };
Then you can open a file in binary mode and write name, then write artist:
size_t len = str.size();
you write the length of the string
file.write(reinterpret_cast<const char*>(&len), sizeof(len));
then write the actual string data:
file.write(str.c_str(), len);


And then You read the same way -> You read the size first, then read that many chars ;)
It would restructure Your code a little bit, but I think it'd be the right way to do this.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! I didn't think it'd be that simple. I'll try it out when I get time. You may have saved me a lot of stress.
1

Doing like this

bin.read(reinterpret_cast<char *>(&new_song), sizeof(Music));

Will not end well, if you look at your Music class

class Music
{
    public:
        Music(string name = "", string artist = "");

    private:
        string m_name;
        string m_artist;
    ...

the read will just overwrite the strings which does not take into account the string template instantiation.

To handle this overload the streaming operators >> and << so that you can in your code read from the file like this:

...
Music song;
bin >> song;
...

To do this add outside your Music class functions to pack and unpack the instance.

E.g.

std::ostream& operator<<(std::ostream& out, const Music &in)
{
  return out << in.m_name.length() << in.m_name.c_str() 
        << in.m_artist.length() << in.m_artist.c_str();
}

std::istream& operator>>(std::istream& is, Music &in)
{
  // pseudo code
  // read length, read characters of name put into "in"
  // read length, read characters of artist put into "in"
}

inside those functions you would convert to the binary format of your choice. A binary file is just data that is interpreted in a raw way and is not really different from a text file which is also is a binary file except one assumes a certain format e.g. readable characters and maybe newline characters.

if you want to store in a binary form you need to figure out a way to store enough information so that you can retrieve it later. e.g. if you store a string you can either store the length of the string first then the characters after that or store the string and have a ending \0 at the end so you know when to stop when reading the string.

1 Comment

Thank you! I'm going to be reading this and trying out everything when I get time to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.