Чтение объектов из файла в cpp - PullRequest
0 голосов
/ 01 мая 2020

У меня есть личный класс и домашнее животное. У человека есть имя и возраст. домашнее животное имеет животное, и его цвет

класс person содержит вектор домашних животных

я пытаюсь прочитать данные из файла, чтобы заполнить вектор объектов person (каждый из которых имеет свой собственный вектор домашних животных)

файл структурирован следующим образом:

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 является «флагом» того, что запись завершена

я не могу понять, как учесть изменчивость # домашних животных для каждого человека, не говоря уже о том, как остановиться на -1

Вот код, который у меня есть до сих пор: (работает с файлом данных, который является линейным / без изменения количества домашних животных)

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);
        ..
    }
}

1 Ответ

0 голосов
/ 01 мая 2020

Это не так сложно. Мы разбим большую проблему на более мелкие, а затем решим эту часть по частям.

Мы будем использовать оператор экстрактора iostream для получения всех данных из потока. Для класса Pet сначала мы прочитаем полную строку с животным и цветом. Для этой цели мы будем использовать наиболее часто используемый std::getline.

Сначала мы всегда читаем всю строку целиком, чтобы избежать проблем с концом строки. Затем мы помещаем только что прочитанную строку в std::istringstream. Это также поток, и с этим мы можем читать из строки, как из любого другого потока.

Из этого потока мы просто извлекаем имя и возраст. Не так сложно.

Чтение человека немного сложнее. Новая запись в файле может начинаться с пустой строки. Поэтому мы сначала прочитаем пустые строки и отбросим их.

Затем снова поместим строку в std::istringstream, а затем извлечем имя и возраст. Простой.

Далее может быть один или несколько домашних животных. Разделитель - это строка, содержащая -1. Итак, мы читаем строки через некоторое время l oop, пока строка не станет -1. В случае -1 мы не будем выполнять тело l oop.

Если это не -1, а действительные данные, мы снова помещаем строку в std::istringstream и извлекаем одного питомца из этот поток. Линия Pet pTemp; iss2 >> pTemp; позвонит оператору по добыче домашних животных. Затем мы добавляем нового питомца к нашему внутреннему std::vector.

Это практически все.

В основном мы открываем файл и проверяем, сработало ли это.

Затем мы определяем переменную типа vector (people) и используем ее конструктор диапазона для ее инициализации. В качестве итератора для конструктора диапазона мы будем использовать std::istream_iterator для класса Person. И это будет просто вызывать экстрактор Person, пока все данные не будут прочитаны. Итак, мы будем читать все данные одним концом.

#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;
}

...