简体   繁体   中英

Read in objects from file in cpp

i have a person class and a pet class. person has a name and an age. pet has an animal and its colour

person class holds an vector of pets

i am trying to read in data from a file to fill an vector of person objects(who each have their own vector of pets)

file structured like so:

sally 32
cat brown
-1

tom 49
dog white
dog brown
-1

sue 54
lizard green
-1

emily 18
cat white
cat brown
cat black
-1

-1 being a "flag" that the entry is finished

i cant figure out how to account for the variability of # of pets for each person, let alone how to stop at -1

Here is the code that I have so far: (works on data file that is linear/without variability in amount of pets)

void fillArray(vector<Person> &people){
    ifstream peopleFile("people.dat");
    string name;
    int age;
    string animal;
    string colour;

    while(!peopleFile.eof()){
        peopleFile>>name>>age;
        peopleFile>>animal>>colour;
        Person p(name,age);
        Pet pet(animal,colour);
        p.addPet(pet);
        people.push_back(p);
        ..
    }
}

It is not so complicated. We will break up the big problem into smaller problems and then solve that part by part.

We will use the iostream extractor operator to get all data from the stream. For the Pet class, we will first read the complete line, with the animal and the color. We will use the most used std::getline for that purpose.

We always read the complete line first to avoid problems with the line end. Then we stuff the just read line into a std::istringstream . This is also a stream, and with that, we can read from a string like from any other stream.

From that stream we simply extract the name and the age. Not so complicated.

Reading a person is a little bit more complex. A new entry in a file could start with an empty line. So we will first read the empty lines and discard them.

Then, again, we put the line in an std::istringstream and then extract the name and the age. Simple.

Next, there are may be one or more Pets. The delimiter is a line containing -1. So, we read lines in a while loop, until the line is -1. In the case of -1, we will not execute the loop body.

If it is not -1, but valid data, we put the line again into a std::istringstream and extract one Pet from this stream. The line Pet pTemp; iss2 >> pTemp; Pet pTemp; iss2 >> pTemp; will call the pets extractor operator. Then we add the new pet to our internal std::vector .

That is basically all.

In main, we open the file and check, if that worked.

Then we define a variable of type vector (persons) and use its range constructor to initialize it. As iterator for the range constructor, we will use the std::istream_iterator for the Person class. And this will simply call the Person extractor, until all data are read. So, we will read all data with a one liner in the end.

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>

struct Pet {
    std::string animal{};
    std::string color{};
};

// Define Extractor for a pet
std::istream& operator >> (std::istream& is, Pet& p) {

    // Read a line containing the animal name and the color
    if (std::string line{}; std::getline(is, line)) {

        // Now put line in istringstream, so that we can use iostream operations to extract data
        std::istringstream iss{ line };

        // Extract info for one pet
        iss >> p.animal >> p.color;
    }
    return is;
}

// Define Inserter for a pet for easier output
std::ostream& operator << (std::ostream & os, const Pet & p) {
    return os << "Pet:   \t" << p.animal << "\t\tAnimal: \t " << p.color << '\n';
}


struct Person {
    std::string name{};
    unsigned int age;
    std::vector<Pet> pets;
};

// Define a extractor for a person
std::istream& operator >> (std::istream& is, Person& p) {
    std::string line{};
    // Read empty strings and discard them
    while (std::getline(is, line) && line == "")
        ; 
    if (is) {
        // Read a line containing the name and the age
        // Now put line in istringstream, so that we can use iostream operations to extract data
        std::istringstream iss{ line };

        // Extract name and age
        iss >> p.name >> p.age;
        p.pets.clear();

        // Next, we want to read all pets, line by line, until we find -1
        while (std::getline(is, line) && line != "-1") {

            // Now put line in istringstream, so that we can use iostream operations to extract data
            std::istringstream iss2{ line };

            // Extract pet from this line. Call overwritten Extractor from Pet
            Pet pTemp; iss2 >> pTemp;

            // Add new pet to vector
            p.pets.push_back(std::move(pTemp));
        }
    }
    return is;
}

// Define Inserter for a person for easier output
std::ostream& operator << (std::ostream& os, const Person& p) {
    os << "\n\nName:\t" << p.name << "\t\tAge: \t" << p.age <<'\n';
    for (const Pet& pet : p.pets) os << pet;
    return os;
}

int main() {
    // Open file and check, if it could be opened
    if (std::ifstream datFileStream{ "r:\\people.dat" }; datFileStream) {

        // Read complete source file with the vectors range constructor
        std::vector persons(std::istream_iterator<Person>(datFileStream), {});

        // Show debug output
        for (const Person& p : persons) std::cout << p;
    }
    else
        std::cerr << "\n\n*** Error: Could not open source file\n";
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM