How do I stream a line of comma separated txt file to an object that contain first name, last name and age variables? I want to overload the >> operator to do such operations.
std::istream& operator>>(std::istream& file, PersonData& obj) {
std::string line, firstName, lastName, age;
while (std::getline(file, line)) {
std::stringstream ss(line);
std::getline(ss,firstName,',');
std::getline(ss,lastName,',');
std::getline(ss, age,',');
firstName >> obj.firstName;
lastName >> obj.lastName;
std::stoi(age) >> obj.age;
}
return file;
}
Your operator>> should read a single entry from the file.
If you have a class
struct Person {
std::string first_name;
std::string last_name;
unsigned age;
};
You can use in/output operators:
std::ostream& operator<<(std::ostream& out,const Person& p) {
out << p.first_name << ", " << p.last_name << ", " << p.age;
return out;
}
std::istream& operator>>(std::istream& in,Person& p){
const auto comma = ',';
std::getline(in,p.first_name,comma);
std::getline(in,p.last_name,comma);
std::string temp;
std::getline(in,temp);
p.age = std::stoi(temp);
return in;
}
And then extract one Person at a time from the file:
int main() {
std::stringstream ss{"Peter, Fish, 42\nLaura, Peterson, 105"};
Person p1,p2;
ss >> p1 >> p2;
std::cout << p1 << "\n" << p2;
};
Output:
Peter, Fish, 42
Laura, Peterson, 105
Live example
To read all entries from a file you can use a vector and a loop:
std::vector<Person> persons;
Person entry;
while(stream >> entry) persons.push_back(entry);
PS: I missed to remove leading blanks from the names. I'll leave that to you ;)
john-wick.txt
John,Wick,50
main.cpp
#include <iostream>
#include <fstream>
struct PersonData {
string firstname;
string lastname;
int age;
friend std::istream& operator>>(std::istream& in, PersonData& obj) {
char comma;
in >> obj.firstname >> comma >> obj.lastname >> comma >> obj.age;
return in;
}
}
int main(int argc, char *argv[]) {
std::ifstream file("john-wick.txt");
PersonData john;
file >> john;
}
Here's a stencil for your class:
class My_Class
{
public:
friend std::istream& operator>>(std::istream& input, My_Class& mc);
private:
// members go here.
};
std::istream& operator>>(std::istream& input, My_Class& mc)
{
// Insert code to read comma separated values here.
return input;
}
The above allows you to stream input to your class:
My_Class mc;
std::vector<My_Class> database;
while (input_file >> mc)
{
database.push_back(mc);
}
Edit 1: Reading CSV
Based on the code you posted, I believe this is what you want:
std::istream& operator>>(std::istream& input, My_Class& mc)
{
std::getline(input, mc.firstName,',');
std::getline(input, mc.lastName,',');
input >> mc.age;
// Ignore till the end of the line:
input.ignore(10000, '\n');
return input;
}
Editing your question with a sample of the input file will allow readers to confirm or deny the content of the function.
Related
This question already has an answer here:
Read lines from text file and store into array
(1 answer)
Closed 1 year ago.
the text file looks something like this:
9528961 Adney Smith CS 4.2
9420104 Annalynn Jones EE 2.6
9650459 Bernadette Williams IT 3.6
...
there are 45 lines in the text file meaning 45 students. I have read the text file and when I run the program I get this:
9428167
Mason
Taylor
CS
4.8
9231599
Alexander
Jones
CS
2.3
My main file looks like this:
int main()
{
auto student = new Student<string>();
std::vector<string> students;
std::ifstream inputFile;
inputFile.open("enroll_assg.txt");
std::string line;
if(inputFile.is_open()){
while(std::getline(inputFile, line)){
std::istringstream iss(line);
std::string word;
while(iss >> word){
std::cout << word << std::endl;
for(int i = 0; i < 5; i++){
}
}
}
}
return 0;
}
Each student has 5 columns (id, fname, lname, department, gpa) and I need make a vector which includes all these student object. I need some help doing this so comments and answers are most welcome. Thank you.
IMHO, the best method is to use a struct or class to model or represent the data record you need to read.
struct Student
{
unsigned int id;
std::string first_name;
std::string last_name;
std::string major_code;
double gpa;
friend std::istream& operator>>(std::istream& input, Student& s);
};
std::istream& operator>>(std::istream& input, Student& s)
{
input >> s.id;
input >> s.first_name;
input >> s.last_name;
input >> s.major_code;
input >> s.gpa;
input.ignore(10000, '\n'); // Synchronize to next line.
return input;
}
Your input code could look like this:
std::vector<Student> database;
Student s;
//... open file.
while (student_file >> s)
{
database.push_back(s);
}
The above code will read each student record into a database, so you can analyze it.
Try something more like this instead:
int main()
{
std::ifstream inputFile("enroll_assg.txt");
if (inputFile.is_open()){
std::vector<Student<string>> students;
std::string line;
while (std::getline(inputFile, line)){
std::istringstream iss(line);
Student<string> student;
iss >> student.id;
iss >> student.fname;
iss >> student.lname;
iss >> student.department;
iss >> student.gpa;
students.push_back(student);
}
// use students as needed...
}
return 0;
}
Then, you should consider having Student overload the operator>>, which will greatly simplify the loop so you can do something more like this instead:
template<typename T>
std::ostream& operator>>(std::ostream &in, Student<T> &student)
{
std::string line;
if (std::getline(in, line))
{
std::istringstream iss(line);
iss >> student.id;
iss >> student.fname;
iss >> student.lname;
iss >> student.department;
iss >> student.gpa;
}
return in;
}
int main()
{
std::ifstream inputFile("enroll_assg.txt");
if (inputFile.is_open()){
std::vector<Student<string>> students;
Student<string> student;
while (inputFile >> student){
students.push_back(student);
}
// use students as needed...
}
return 0;
}
First this question is a duplicate of Read lines from text file and store into array which already has the answer you're looking for in this question.
The below shown program uses struct to represent a given Student and it also used a std::vector. You can use this program as a reference(starting point). It reads student information from the input text file and store that information in a vector of Student.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
//this class represents a Student
class Student
{
public:
std::string firstName, lastName, courseName ;
unsigned long id = 0;
float marks = 0;
};
int main()
{
std::ifstream inputFile("input.txt");
std::string line;
std::vector<Student> myVec;//create a vector of Student objects
if(inputFile)
{
while(std::getline(inputFile, line))
{
Student studentObject;
std::istringstream ss(line);
//read the id
ss >> studentObject.id;
//read the firstname
ss >> studentObject.firstName;
//read the lastname
ss >> studentObject.lastName;
//read the courseName
ss >> studentObject.courseName;
//read the marks
ss >> studentObject.marks;
if(ss)//check if input succeded
{
myVec.emplace_back(studentObject);//add the studentObject into the vector
}
}
}
else
{
std::cout<<"File cannot be opened"<<std::endl;
}
//lets print out the elements of the vecotr to confirm that all the students were correctly read
for(const Student &elem: myVec)
{
std::cout << elem.id << ": "<<elem.firstName<<" "<<elem.lastName<<" "<<elem.courseName<<" "<<elem.marks <<std::endl;
}
return 0;
}
The output of the above program can be seen here.
Suppose I have a struct like this:
struct Person
{
string fName;
string lName;
int age;
};
And I want to read in a file(ppl.log) like this:
Glenallen Mixon 14
Bobson Dugnutt 41
Tim Sandaele 11
How would I read in the file and store them?
This is what I have
int main()
{
Person p1, p2, p3;
ifstream fin;
fin.open("ppl.log");
fin >> p1;
fin >> p2;
fin >> p3;
return 0;
}
Does that read in the entire line? Or do I have to use the getline()?
I recommend overloading operator>>:
struct Person
{
string fName;
string lName;
int age;
friend std::istream& operator>>(std::istream& input, Person& p);
};
std::istream& operator>>(std::istream& input, Person& p)
{
input >> p.fName;
input >> p.lName;
input >> p.age;
input.ignore(10000, '\n'); // Align to next record.
return input;
}
This allows you to do things like this:
std::vector<Person> database;
Person p;
//...
while (fin >> p)
{
database.push_back(p);
}
Your fields are space separated, so you don't need to use getline. The operator>> for strings will read until a whitespace character.
I have overloaded the >> operator, making an object from a stream. I was wondering if I can use this in a contructor that takes in the same format as the stream but as a string. Could I use the >> operator in the constructor, or will I have to make code to split that line up differently?
For example:
Person::Person(std::string line)
{
// this doesn't work
this >> line;
}
std::istream &operator>>(std::istream &is, Person &p)
{
char c1;
std::string forename, surname;
if (is >> forename >> c1 >> surname)
{
if (c1 == ',')
{
p.forename = forename;
p.surname = surname;
}
else
{
is.clear(std::ios_base::failbit);
}
}
return is;
}
An example input would be: Foo,Bar
#include <sstream>
Person::Person(std::string line)
{
std::stringstream ss(line);
ss >> *this;
}
I'm wanting to take 4 values, each separated by a comma, and store them to a airport object. Each line would have those 4 values would be stored together. So, calling all objects from Sweden, would find all objects from the country sweden and retrieve the appropriate values as well. Right now I have 4 different vectors, which store all 4 object values, however, doesn't necessarily set all 4 attributes/values together as an object. Help is greatly appreciated.
Examples of what the Text File looks like -
1,Goroka,Goroka,Papua New Guinea,GKA,AYGA,-6.081689,145.391881,5282,10,U
2,Madang,Madang,Papua New Guinea,MAG,AYMD,-5.207083,145.7887,20,10,U
#include "pch.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
using namespace std;
struct Airport
{
string code;
string name;
string city;
string nation;
friend std::istream& operator>>(std::istream& input, Airport& a);
};
istream& operator>>(std::istream& input, Airport& a)
{
getline(input, a.code, ',');
getline(input, a.name, ',');
getline(input, a.city, ',');
getline(input, a.nation);
return input;
}
////////////////////////////////////////////
vector<string> split(const string& s, const string& delim)
{
const bool keep_empty = true;
vector<string> result;
if (delim.empty())
{
result.push_back(s);
return result;
}
string::const_iterator substart = s.begin(), subend;
while (true)
{
subend = search(substart, s.end(), delim.begin(), delim.end());
string temp(substart, subend);
if (keep_empty || !temp.empty())
{
result.push_back(temp);
}
if (subend == s.end())
{
break;
}
substart = subend + delim.size();
}
return result;
}
// Sorting Function
bool Sort_By_Name_Ascending(const Airport& a, const Airport& b)
{
return a.name < b.name;
}
int main()
{
vector<Airport> database;
Airport a;
char choice;
string chr;
ifstream inputFile;
inputFile.open("airports.dat");
if (!inputFile)
{
cout << "File Access Error!";
return 0;
}
string fileLine;
cout << "Reading File ..." << endl;
while (!inputFile.eof())
{
getline(inputFile, fileLine);
vector<string> lineVector = split(fileLine, ",");
if (lineVector[4].length() == 3)
{
while (inputFile >> a)
{
database.push_back(a);
}
}
}
cout << "What would you like to do?\nA. Sort in Alphabetical Order.\nB.
Delete an Airport.\nC. Exit Program." << endl;
cin >> choice;
switch (choice)
{
case 'A':
sort(database.begin(), database.end(), Sort_By_Name_Ascending);
break;
case 'B':
cout << "Deleting a value" << endl;
break;
case 'C':
return 0;
break;
}
return 0;
}
You may want to organize your data structures to model the input data:
struct Airport
{
std::string code;
std::string name;
std::string city;
std::string nation;
};
A next step is to overload operator>> to read in an instance from a stream:
struct Airport
{
//...
friend std::istream& operator>>(std::istream& input, Airport& a);
};
std::istream& operator>>(std::istream& input, Airport& a)
{
std::getline(input, a.code, ',');
std::getline(input, a.name, ',');
std::getline(input, a.city, ',');
std::getline(input, a.nation);
return input;
}
This simplifies the input:
std::vector<Airport> database;
Airport a;
while (inputfile >> a)
{
database.push_back(a);
}
To sort, you can come up with some functions or function objects and supply them to std::sort:
bool Sort_By_Name_Ascending(const Airport& a, const Airport& b)
{
return a.name < b.name;
}
//...
std::sort(database.begin(), database.end(), Sort_By_Name_Ascending);
Edit 1: The whole record or line
There is a difference between modeling the whole line versus only fields you are interested in. If your records are text line (one record == one text line), you may want to read in the text line as a string then extract the fields you are interested in.
std::istream& operator>>(std::istream& input, Airport& a)
{
std::string record;
std::getline(input, record);
// Now extract the interesting fields from the string.
std::istringstream record_stream;
unsigned int record_number;
char comma;
record_stream >> record_number; // Read but ignore.
record_stream >> comma; // Read but ignore.
std::getline(record_stream, a.code, ',');
std::getline(record_stream, a.name, ',');
std::getline(record_stream, a.city, ',');
std::getline(record_stream, a.nation);
return input;
}
I have a file that contains employee information on each line (id, department, salary, and name). Here is an example line:
45678 25 86400 Doe, John A.
Right now I am reading in each word using fstream, which works until I get to the name part. My question is what is the easiest way to capture that name as a whole?
Data >> Word;
while(Data.good())
{
//blah blah storing them into a node
Data >> Word;
}
You probably want to define a struct to hold the data for an employee, the define an overload of operator>> to read one of those records from your file:
struct employee {
int id;
int department;
double salary;
std::string name;
friend std::istream &operator>>(std::istream &is, employee &e) {
is >> e.id >> e.department >> e.salary;
return std::getline(is, e.name);
}
};
int main() {
std::ifstream infile("employees.txt");
std::vector<employee> employees((std::istream_iterator<employee>(infile)),
std::istream_iterator<employee>());
// Now all the data is in the employees vector.
}
#include <fstream>
#include <iostream>
int main() {
std::ifstream in("input");
std::string s;
struct Record { int id, dept, sal; std::string name; };
Record r;
in >> r.id >> r.dept >> r.sal;
in.ignore(256, ' ');
getline(in, r.name);
std::cout << r.name << std::endl;
return 0;
}
I would create a record and define the input operator
class Employee
{
int id;
int department;
int salary;
std::string name;
friend std::istream& operator>>(std::istream& str, Employee& dst)
{
str >> dst.id >> dst.department >> dst.salary;
std::getline(str, dst.name); // Read to the end of line
return str;
}
};
int main()
{
Employee e;
while(std::cin >> e)
{
// Word with employee
}
}