1

So I am working on a project for school. I am making the game "Lemonade Stand" as a text based console game. I am having trouble reading data from a save file and storing it in the appropriate place. The problem is that we output all of our data to one text file and then in that text file break it up into multiple lines. I want to read through the file one line at a time and spit the contents of the string back out into the variables. This will take some variable converting which I am also in need of assistance with.

void loadGame(){

    string playerName;
    int funds, supplies, turn;

    ifstream data ("data.lms");
    if (data.is_open())
    {
        while ( data.good() )
        {

            if (data >> playerName)
                Player.setPlayerName(playerName);
            else
                cout << "Couldn't read player name value.\n";

            if (data >> funds)
                Player.setFunds(funds);
            else
                cout << "Couldn't read funds value.\n";

            if (data >> supplies)
                Player.setSupplies(supplies);
            else
                cout << "Couldn't read supplies value.\n";

            if (data >> turn)
                Player.setTurn(turn);
            else
                cout << "Couldn't read turns value.\n";

        }
        data.close();
    }
}

My thought is that this may be too difficult right now and it may be better to store each variable in its own file and just read from those separately rather than having to parse through one file. Any tips or answers would be much appreciated.

Colby Chaffin
  • 11
  • 1
  • 3
  • 1
    [`while ( data.good() )` is the the same as `while ( !data.bad() )` and still won't work.](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) TL:DR you can't tell if what you read is good or bad until after you read it. – user4581301 Dec 11 '15 at 00:59
  • If all player data is on one line, this can be solved with [`std::getline`](http://en.cppreference.com/w/cpp/string/basic_string/getline) and [`std::stringstream`](http://en.cppreference.com/w/cpp/io/basic_stringstream). See here: [Read file line by line](http://stackoverflow.com/questions/7868936/read-file-line-by-line) – user4581301 Dec 11 '15 at 01:02

2 Answers2

3

You can try std::getline and std::stringstream.

First include sstream.

#include <sstream>

Then you can read data line by line and split into strings.

string line;
while(getline(data, line)) {
    stringstream ss(line);
    string name;
    int funds, supplies, turns;
    ss >> name >> supplies >> turns;
}
David Fang
  • 1,717
  • 1
  • 14
  • 19
1

I am going to go a bit further than David Fang's answer to demonstrate some basic error checking and storage of the players once read.

Note: Starter code lifted from the semi-official answer for this sort of question, Read file line by line.

#include <sstream>
#include <string>
#include <vector>

Need a few includes for this function

std::vector<Player> loadGame()

Define a function that loads all player data from the file into a std::vector. Why a vector? If the game has multiple players, we've got to put them somewhere, and vector is a damn easy somewhere.

{
    std::vector<Player> players; 
    std::string line;
    while (std::getline(infile, line))  

Read in a line, exit if no line. getline documentation.

    {
        std::string playerName;
        int funds, supplies, turn;
        std::istringstream iss(line); 

Load the line into an easily parsed stream. Use is exactly the same as with cin or infile

        if (iss >> playerName >> funds >> supplies >> turn) 

Read in all expected input on line. If there are any errors, skip to else case to clean up. Note: Doesn't handle unexpected input on line. We read in all input here rather than later because there is no point building a Player if the input is bad.

If any of the variables cannot be read and converted to the correct type, the stream will be set into an error state. The if will check this state just as if you'd called good(). If the input is good, we can set up the Player all in one call to the constructor rather than having a bunch of calls to the setter. This way anyone using a Player knows it is all set up and ready to go and merely allocated. With complex objects and multi-threaded systems this is important.

Also the Player's name is not going to change part-way through the game, so a setter function for name is dodgy.

        {  
            Player player(playerName, funds, supplies, turn);
            players.push_back(player);

Make a Player and put player in vector, or

            players.emplace_back(playerName, funds, supplies, turn);

Make a Player directly in the vector.

        }
        else
        { 

One or more errors occurred reading the line. Do whatever you need to do to handle the error

        }
    }
    return players;
}

Return the loaded game to the caller. This may look a little odd. We're passing a list that could be huge to the caller by value. This could mean a very large and expensive copy operation.

Fortunately compilers are not dumb. They know we'll never be using players in this function again and that it is a local variable and thus will be destroyed as soon as the function goes out of scope. The compiler can do anything it wants to the vector, because this vector is effectively dead. For example, it can rip the vector's guts out and transplant them into another vector in the caller's scope without copying anything and leave the vector for the digital crows to gobble up.

Usage of the function is:

std::vector<Player> players = loadGame();

All together without the running commentary:

#include <sstream>
#include <string>
#include <vector>

std::vector<Player> loadGame()
{
    std::vector<Player> players; 
    std::string line;
    while (std::getline(infile, line))  
    {
        string playerName;
        int funds, supplies, turn;
        std::istringstream iss(line); 

        if (iss >> playerName >> funds >> supplies >> turn) 
        {  
            // Player player(playerName, funds, supplies, turn);
            // players.push_back(player);
            players.emplace_back(playerName, funds, supplies, turn);
        }
        else
        { 
            // handle error
        }
    }
    return players;
}
Community
  • 1
  • 1
user4581301
  • 29,019
  • 5
  • 26
  • 45