I have a file with comma separated values
M,21,Hazel
F,49,Stephen
I am sending ifstream into a function that takes in istream to read the line.
ifstream file(fileName);
char gender;
file.get(gender);
file.ignore(); // ignore comma
if (gender == 'M') {
Gender* tmp = new Male;
file >> *tmp;
} else if (gender == 'F') {
Gender* tmp = new Female;
file >> *tmp;
}
The first character up to the comma is read correctly, but when I send it to read it asks for user input when it is not wanted. It doesnt read the rest of the file ie "49,Stephen"
istream& operator>>(istream& istr, ReadW& ref) {
return ref.read(istr);
}
istream& read(istream& is) {
char tName[16];
is >> age;
is.ignore(); // ignore comma
is.getline(tName, 16, ',');
}
Basically you are asking about reading a csv file and splitting it into tokens. If you search for this here on SO, you will find many many posts which explain how to do.
But in your case it maybe simpler. If it is guaranteed that the source file has exactly the format abouve, without additional spaces before or after the comma, then you can use the standard extractor mechanisms for streams. You just will read the comma into a dummy variable. This may look like:
// 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) {
This will read the first character int the variable gender
and then read the comma and put it into the comma
variable. This we will simply not use. Then, we will continue to extract more values in the same way.
If the structure of the source file is different, it will not work. For example, if the first variable would be a std::string
, then testDataStream >> someString
would read the comlplete line up to the next white space. But, with the existing structure it will work.
Anyway, you can use also a different functionality whi,ch will give you a little bit more security in case of malformed input lines. And that is, first read a complete line, then put it into a std::istringstream
and extract the data from that.
If you would have complete different data structure, then you would probaly use the approach with std::getline
with delimiter or the std::regex_token_iterator
. But this would be to much for here.
Additionally, you obviously have a class hierachy. And you create derived classes based on a value read at runtime. This is usually solved with the Abstract Factory pattern.
I created a workable example, where you can see all these mechanisms. Please note: I will not use Plain C-Style char
arrays for strings. And, I will never use raw pointers for owned memory. For that smart pointers shall be used.
#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;
}
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.