По сути, вы спрашиваете о чтении CSV-файла и разбиении его на токены. Если вы ищете это здесь на SO, вы найдете много постов, которые объясняют, как это сделать.
Но в вашем случае это может быть проще. Если гарантируется, что исходный файл имеет точно такой же формат, без дополнительных пробелов до или после запятой, то вы можете использовать стандартные механизмы извлечения для потоков. Вы просто прочитаете запятую в фиктивную переменную. Это может выглядеть так:
// What we want to read
char gender{};
unsigned int age{};
std::string name{};
// This is a dummy
char comma{};
while (testDataStream >> gender >> comma >> age >> comma >> name) {
Это будет читать первый символ в переменной gender
, а затем читать запятую и помещать ее в переменную comma
. Это мы просто не будем использовать. Затем мы продолжим извлекать больше значений таким же образом.
Если структура исходного файла отличается, он не будет работать. Например, если первая переменная будет std::string
, то testDataStream >> someString
будет читать полную строку до следующего пробела. Но с существующей структурой это будет работать.
В любом случае, вы можете использовать и другую функциональность, которая обеспечит вам немного больше безопасности в случае неправильно сформированных входных линий. Иными словами, сначала прочитайте полную строку, затем поместите ее в std::istringstream
и извлеките из нее данные.
Если бы у вас была полная структура данных, то вы, вероятно, использовали бы подход с std::getline
с разделителем или std::regex_token_iterator
. Но это было бы очень много для здесь.
Кроме того, у вас, очевидно, есть иерархия классов. И вы создаете производные классы на основе значения, прочитанного во время выполнения. Обычно это решается с помощью шаблона Abstract Factory.
Я создал работоспособный пример, где вы можете увидеть все эти механизмы. Пожалуйста, обратите внимание: я буду не использовать простые C -Style char
массивы для строк. И я никогда не буду использовать необработанные указатели для собственной памяти. Для этого должны использоваться умные указатели.
#include <iostream>
#include <sstream>
#include <memory>
#include <map>
#include <functional>
std::istringstream testDataStream(R"(F,21,Hazel
M,49,Stephen)");
// Abstract base class "Person"
struct Person {
// Constructor
Person(const unsigned int a, const std::string& n) : age(a), name(n) {}
// Do not forget the virtual destructor
virtual ~Person() { std::cout << "Destructor Base\n\n\n"; }
// The data: name and age
unsigned int age{};
std::string name{};
// Since the inserter is not none member function, we will write
// a separate virtual function to do the job polymorph
virtual std::ostream& output(std::ostream& os) const = 0;
// Inserter, will call our internal output function
friend std::ostream& operator << (std::ostream& os, const Person& p) {
return p.output(os);
}
};
// Derived class for a male Person
struct Male : public Person {
// Constructor
Male(const unsigned int age, const std::string& name) : Person(age, name) {}
virtual ~Male() { std::cout << "Destructor Male\n"; }
// And output
virtual std::ostream& output(std::ostream& os) const override {
return os << "Male Person:\nAge:\t" << age << "\nName:\t" << name << '\n';
}
};
// Derived class for a female Person
struct Female : public Person {
// Constructor
Female(const unsigned int age, const std::string& name) : Person(age, name) {}
virtual ~Female() { std::cout << "Destructor Female\n"; }
// And output
virtual std::ostream& output(std::ostream& os) const override {
return os << "Female Person:\nAge:\t" << age << "\nName:\t" << name << '\n';
}
};
// "Creation" Functions for abstract factory
std::unique_ptr<Person> createMale(const unsigned int age, const std::string& name) { return std::make_unique<Male>(age, name); }
std::unique_ptr<Person> createFemale(const unsigned int age, const std::string& name) { return std::make_unique<Female>(age, name); }
// Abstract factory
struct AbstractFactory {
// Abbreviation for finding
using Map = std::map<char, std::function<std::unique_ptr<Person>(const unsigned int, const std::string&)>>;
Map creationFunctions{
{'F', createFemale },
{'M', createMale }
};
std::unique_ptr<Person> create(const char selector, const unsigned int age, const std::string& name) {
// If the selector is in the map
if (Map::iterator searchResult{ creationFunctions.find(selector) }; searchResult != creationFunctions.end())
// Then we call the corresponding creation function
return creationFunctions[selector](age, name);
else
// No key found, the return nullptr (Empty Person());
return std::unique_ptr<Person>();
}
};
// Our abstract factor
AbstractFactory abstractFactory{};
// Driver code
int main() {
// What we want to read
char gender{};
unsigned int age{};
std::string name{};
// This is a dummy
char comma{};
std::string line{};
//#define DIRECT_READ
#ifdef DIRECT_READ
// As long as there is data in the stream, read the gender and the comma
while (testDataStream >> gender >> comma >> age >> comma >> name) {
#else
while (std::getline(testDataStream, line)) {
std::istringstream iss{ line };
if (iss >> gender >> comma >> age >> comma >> name) {
#endif
// Create a Person, either male or female
std::unique_ptr<Person> person = abstractFactory.create(gender, age, name);
// Polymorphism. Call the adequate member function
std::cout << *person << '\n';
// Do other stuff
// . . .
}
#ifndef DIRECT_READ
}
#endif
return 0;
}