Это не так сложно. Мы разбим большую проблему на более мелкие, а затем решим эту часть по частям.
Мы будем использовать оператор экстрактора 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;
}