Reading from a struct from a binary file and inserting in a vector/STL::list and then returning it

1

I am reading a structure named "Highscore" from a binary file. I print the read data in the console and it looks fine. After that, I insert them into a vector with the push_back method and when I'm done, I return the vector.

After the return line, I get the following error (translated from French): "Exception not handled at 0x00007FF6223FF017 in Project1.exe : 0xC0000005 : access violation while reading from 0xFFFFFFFFFFFFFFFF.".

Here is my code:

struct Highscore{
    string username;
    int points;
};

vector<Highscore> ReadFromHighscores(){

    fstream highscoresFiles;
    highscoresFiles.open("highscores.db", ios::in | ios::out | ios::binary | ios::app);

    highscoresFiles.seekg(0);

    Highscore output;
    vector<Highscore> highscores;

    highscoresFiles.read(reinterpret_cast<char *>(&output), sizeof(Highscore));
    cout << "Read : " << output.points << output.username << endl;
    Highscore temp;
    temp.points = output.points;
    temp.username = output.username;


    if (!highscoresFiles.eof()) {
        highscores.push_back(temp);
    }


    highscoresFiles.close();

    return highscores;
}
c++
asked on Stack Overflow Dec 3, 2018 by MadBrother33

1 Answer

0

As having been pointed out, std::string contains a char* (which you are saving and restoring) to the real data. The actual data is not saved and the pointer you restore is causing the crash. To fix that you can create operators for streaming to/from files and dump the username c-string (including the terminating \0) and then write the points int raw. This should work as long as your usernames don't contain \0 in the middle of the string (unusual but valid).

#include <iostream>
#include <iomanip>
#include <vector>
#include <fstream>

struct Highscore{
    std::string username;
    int points;

    Highscore(const std::string& n, int p) : username(n), points(p) {}
    Highscore() : Highscore("",0) {}
    // operator to save a Highscore to file
    friend std::ofstream& operator<<(std::ofstream&, const Highscore&);
    // operator to read a Highscore from file
    friend std::ifstream& operator>>(std::ifstream&, Highscore&);
    // operator to print a Highscore on other ostreams (like std::cout)
    friend std::ostream& operator<<(std::ostream&, const Highscore&);
};

std::ofstream& operator<<(std::ofstream& os, const Highscore& hs) {
    os.write(hs.username.c_str(), hs.username.size()+1);
    os.write(reinterpret_cast<const char*>(&hs.points), sizeof(hs.points));
    return os;
}

std::ifstream& operator>>(std::ifstream& is, Highscore& hs) {
    std::getline(is, hs.username, '\0');
    is.read(reinterpret_cast<char*>(&hs.points), sizeof(hs.points));
    return is;
}
std::ostream& operator<<(std::ostream& os, const Highscore& hs) {
    os << std::setw(15) << std::left << hs.username << std::setw(8) << std::right << hs.points;
    return os;
}

int main() {
    // create "highscores.db"
    {
        std::vector<Highscore> highscores {
            {"MadBrother33", 234234},
            {"Barny", 14234},
            {"Bart", 1434}
        };
        std::ofstream out("highscores.db", std::ios::binary);
        for(auto& hs : highscores) {
            out << hs;
        }
    }
    // read "highscores.db"
    {
        std::vector<Highscore> highscores;
        {
            Highscore hs;
            std::ifstream in("highscores.db", std::ios::binary);
            while(in>>hs) {
                highscores.emplace_back(std::move(hs));
            }
        }
        for(auto& hs : highscores) {
            std::cout << hs << "\n";
        }
    }
}

Output

MadBrother33     234234
Barny             14234
Bart               1434
answered on Stack Overflow Dec 3, 2018 by Ted Lyngmo • edited Dec 3, 2018 by Ted Lyngmo

User contributions licensed under CC BY-SA 3.0