Parsing CSV into Struct Array - c++

I am trying to parse CSV into Array of structures. Delimiter is ';'. If you look into the code it seems that I filled only first element of the array out of nine that is 9 lines are in my CSV right now. Remaining elements of the array are just 0. Anyone got some advice? Thanks
fileTO.open("imput.csv", ios_base :: app);
fileFROM.open("imput.csv", ios_base :: app);
//IntoCsvThruConsole();
// array of structures from csv
string line;
string sID, stype, scategory, samount, sdate;
int lines = CountExistingLines();
Properties * structure = new Properties[lines];
int counter = 0;
int position = 0;
while (getline(fileFROM, line))
{
sID = "";
samount = "";
for (int i = 0; i < line.size(); i++)
{
if (line[i] == ';')
{
position++;
continue;
}
switch (position)
{
case 0 : sID = sID + line[i];
break;
case 1 : structure[counter].type = structure[counter].type + line[i];
break;
case 2 : structure[counter].category = structure[counter].category + line[i];
break;
case 3 : samount = samount + line[i];
break;
case 4 : structure[counter].date = structure[counter].date + line[i];
break;
}
}
structure[counter].ID = atoi(sID.c_str());
structure[counter].amount = atoi(samount.c_str());
cout << "ID zaznamu: " << structure[counter].ID << endl;
cout << "Typ: " << structure[counter].type << endl;
cout << "Kategorie: " << structure[counter].category << endl;
cout << "Castka: " << structure[counter].amount << endl;
cout << "Datum: " << structure[counter].date << endl;
counter++;
}
delete[] structure;
I have globally initialized struct correctly and also fstreams. Hope it is enough. Thanks
enter image description here

I recommend using Boost.Spirit for such parsing tasks.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
struct Date
{
int day;
int month;
int year;
};
std::ostream& operator<<(std::ostream& os, Date const &d)
{
os << d.day << '/' << d.month << '/' << d.year;
return os;
}
BOOST_FUSION_ADAPT_STRUCT(
Date,
day, month, year)
struct Properties
{
int ID;
std::string type;
std::string category;
int amount;
Date date;
};
BOOST_FUSION_ADAPT_STRUCT(
Properties,
ID, type, category, amount, date)
std::vector<Properties> parse(std::string const &input)
{
auto iter = input.begin();
using namespace boost::spirit::x3;
auto name = rule<class name, std::string>{}
= lexeme[alpha >> *alpha];
auto date = rule<class date, Date>{}
= int_ > '/' > int_ > '/' > int_;
std::vector<Properties> result;
bool r = phrase_parse(iter, input.end(),
(int_ > ';' > name > ';' > name > ';' > int_ > ';' > date) % eol,
space - eol, result);
if (!r)
{
std::string rest(iter, input.end());
throw std::invalid_argument("Parsing failed at " + rest);
}
return result;
}
int main()
{
// This could be a file instead with std::ifstream
std::istringstream input;
input.str(
"1;TypeA;CategoryA;10;05/12/2017\n"
"2;TypeB;CategoryA;21;04/12/2017\n"
"3;TypeB;CategoryB;19;01/12/2017\n"
"4;TypeA;CategoryA; 6;20/11/2017\n");
std::vector<Properties> entry = parse(input.str());
for (auto e : entry)
{
std::cout << "Found the following entries:\n"
<< " ID: " << e.ID << "\n"
<< " Type: " << e.type << "\n"
<< " Category: " << e.category << "\n"
<< " Amount: " << e.amount << "\n"
<< " Date: " << e.date << "\n";
}
}
Live example

Related

C++ Read Value from a pointer to struct including the target struct in an array

I am new to c++ and trying to make an atm. It consits of one file reading user data from files and a main file. Everytime I try to read the output of the returned pointer to the array of structs, where the data is stored, I get strange data. I think there is a problem when assigning strings to the first struct, because when I tried with test values (third file) it worked.
Edit:
I can read out integeres from the strcut, but I have problems with strings.
Here is the code
users.cpp (Creates the struct, included in main.cpp)
#include <fstream>
#include <string>
#include <iostream>
#define STRING_ERROR 4294967295
#define MAX_USERS 100000
using namespace std;
struct user
{
int id;
string firstname;
string name;
int age;
int pin;
};
struct user_container{
int size;
user users [MAX_USERS];
};
typedef struct user User;
typedef struct user_container container;
void print_user_vars(User *user){
cout << "Id: " << user->id << "\nFirstname: " << user->firstname << "\nName: " << user->name << "\nAge: " << user->age << "\nPIN: " << user->pin << endl;
}
int get_usercount(string path){
int usercount = 0;
ifstream file;
string line;
file.open(path, ios::binary | ios::in);
while(true){
getline(file , line);
if(file.fail())
break;
if(line.find("}") != STRING_ERROR){
usercount++;
}
}
file.close();
return usercount;
}
container * get_users(string path){
const int usercount = get_usercount(path);
ifstream file;
string line;
User users[usercount];
const char *values[5] = {"id", "firstname", "name", "age", "pin"};
file.open(path, ios::binary | ios::in);
User proto_user;
int user_num = 0;
while(true){
getline(file , line);
if(file.fail())
break;
if(line.find(":") != STRING_ERROR){
for(int i = 0; i < 5; i++){
if(line.find(values[i]) != STRING_ERROR){
string value;
for(int v = 0; v < line.length(); v++){
if(v > line.find_first_of(":")){
value += line[v];
}
}
if(values[i] == "id"){
proto_user.id = stoi(value);
}
else if(values[i] == "firstname"){
proto_user.firstname = value;
}
else if(values[i] == "name"){
proto_user.name = value;
}
else if(values[i] == "age"){
proto_user.age = stoi(value);
}
else if(values[i] == "pin"){
proto_user.pin = stoi(value);
}
break;
}
}
}
else if(line.find("}") != STRING_ERROR){
//print_user_vars(&proto_user);
users[user_num++] = proto_user;
//cout << "Added user at " << user_num << endl;
}
//cout << line << endl;
}
for(int i = 0; i < user_num; i++){
//cout << "\nUSER: " << i + 1 << endl;
//print_user_vars(&users[i]);
}
static container con;
con.size = usercount;
for(int i = 0; i < usercount; i++){
if(i <= MAX_USERS){
// con.users[i] = users[i] didnt work, but this does
con.users[i].firstname = users[i].firstname;
con.users[i].age = users[i].age;
con.users[i].name = users[i].name;
con.users[i].pin = users[i].pin;
con.users[i].id = users[i].id;
}
}
print_user_vars(&con.users[0]);
return &con;
}
main.cpp (Calls the function)
#include <iostream>
#include "includes/users.h"
using namespace std;
int main(void){
string wasd = "w";
int id;
int pin;
cout << "\n Welcome\n\n ID\n>>> ";
cin >> id;
cout << " PIN\n>>> ";
cin >> pin;
container *con = get_users("users");
int size = con->size;
cout << "Age: " << con->users[0].age << ", PIN: " << con->users[0].pin << ", Firstname: " << con->users[0].firstname << ", Name: "
<< con->users[0].name << ", ID: " << con->users[0].id << endl;
//Functionality
return 0;
}
Simpler.cpp (Just like users.cpp, but with test values, included in main.cpp)
#include <fstream>
#include <string>
#include <iostream>
#define STRING_ERROR 4294967295
#define MAX_USERS 100000
using namespace std;
struct user
{
int id;
string firstname;
string name;
int age;
int pin;
};
struct user_container{
int size;
user users [MAX_USERS];
};
typedef struct user User;
typedef struct user_container container;
container * get_users(string path){
const int usercount = 2;
User users[usercount];
users[0].age = 59;
users[0].firstname = "Peter";
users[0].name = "Bremer";
users[0].id = 456878;
users[0].pin = 1234;
users[1].age = 8;
users[1].firstname = "a";
users[1].name = "b";
users[1].id = 456;
users[1].pin = 1111;
static container con;
con.size = usercount;
for(int i = 0; i < usercount; i++){
if(i <= MAX_USERS){
// con.users[i] = users[i] didnt work, but this does
con.users[i].firstname = users[i].firstname;
con.users[i].age = users[i].age;
con.users[i].name = users[i].name;
con.users[i].pin = users[i].pin;
con.users[i].id = users[i].id;
}
}
cout << "Usercount " << usercount << endl;
return &con;
}
int main(void){
container *con = get_users("users");
int size = con->size;
cout << "Age: " << con->users[0].age << ", PIN: " << con->users[0].pin << ", Firstname: " << con->users[0].firstname << ", Name: "
<< con->users[0].name << ", ID: " << con->users[0].id << endl;
return 0;
}
Not really the answer you are looking for:
Avoid using typedef when you don't need it. Just name your struct User, it will be available under that name without writing struct User.
And for your container, just use one of the STL containers. std::vector<User> would be a good start. This also removes the need for MAX_USERS.
struct User
{
int id;
std::string firstname;
std::string name;
int age;
int pin;
};
typedef std::vector<User> container;
With the three interfaces in user.cpp you can now just use
void print_user_vars(const User& user);
int get_usercount(const std::string& path);
container get_users(const std::string& path);
and thus get rid of the static container con; in get_users.
With a std::vector I no longer see a need for the get_usercount function.
And instead of macros go for constants (and remove magic numbers):
auto const STRING_ERROR = std::string::npos;
(Though I don't agree with the name STRING_ERROR. It's more a STRING_NOT_FOUND.)
Cleanup of print_user_vars after signature change:
void print_user_vars(const User& user)
{
std::cout << "Id: " << user.id << "\nFirstname: " << user.firstname
<< "\nName: " << user.name << "\nAge: " << user.age
<< "\nPIN: " << user.pin << std::endl;
}
And getting some inspiration from get_usercount (a method I don't believe you need):
int get_usercount(const std::string& path)
{
int usercount = 0;
std::ifstream file(path, std::ios_base::binary);
std::string line;
while(std::getline(file, line))
{
if(line.find("}") != STRING_ERROR)
{
usercount++;
}
}
return usercount;
}
This line in get_users is NOT C++. (It's part of the C99 standard):
User users[usercount];
And as #bruno points out:
else if(values[i] == "name"){
does not work. Consider
else if(std::strcmp(values[i], "name") == 0){

Program eating a first letter of a line

So I'm practicing working with files in C++ a bit. I've created a simple program that loads Student's name, index number, date of birth and grades from STUDENT.txt, counts the student's average, and writes it all down in a form below to a different file.
It all works like a charm, except one thing: It eats up the first letter of every line. EXAMPLE:
I have a STUDENT.txt file that looks like this:
Alice Cooper
225883
21/6/1986
6,6,8,9,10
Zakk Wylde
27568
14/5/1978
6,6,6,6,6
So the first student, Cooper, will be processed correctly, but everyone else in the file won't. They'll be written down as 'akk Wylde'...
So everything but a name is OK.
I was hoping someone could tell me what exactly is going on, I'm guessing it's eating up a '\n' and another character, but I couldn't find it with debugging.
// Student_datoteka.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
std::string toString(int day, int month, int year) {
std::string d, m, g, ret;
d = std::to_string(day);
m = std::to_string(month);
g = std::to_string(year);
ret += d;
ret += "/";
ret += m;
ret += "/";
ret += g;
return ret;
}
int main()
{
std::fstream load("STUDENTI.txt", std::ios::in);
std::ofstream write("IZVJESTAJ.txt");
write << std::setw(30) << std::left << "Student"
<< std::setw(10) << "Indeks"
<< std::setw(20) << "Datum rodjenja"
<< std::setw(10) << "Prosjek" << std::endl;
write << std::setw(30) << std::left << "-------"
<< std::setw(10) << "------"
<< std::setw(20) << "--------------"
<< std::setw(10) << "-------" << std::endl;
if (!write)
std::cout << "ERROR!";
std::string name;
int indeks(0), day(0), month(0), year(0);
char sign(0), sign2(0);
int grades[30];
double average(0);
while (std::getline(load, name)) {
if (load.eof()) break;
load >> indeks;
load >> day >> sign >> mjesec >> sign2 >> year;
int i(0);
while (load >> grades[i]) {
load >> sign;
average += grades[i];
i++;
}
average /= double(i);
std::string datum = toString(day, month, year);
write << std::setw(30) << std::left << name
<< std::setw(10) << indeks
<< std::setw(20) << datum
<< std::setw(10) << std::setprecision(2)
<< average<< std::endl;
average = 0;
if (load.eof()) break;
load.clear();
}
return 0;
}
This is the output: http://prntscr.com/jvm3jn
I have made a small improvements on your code so please, if you don't like, take only the working part. Note that my code can be improved and generalized even more but I am leaving that to you.
Hope you would like my solution:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
typedef struct Student
{
std::string name;
int index_no;
int day_of_birth;
int month_of_birth;
int year_of_birth;
std::vector<int> grades;
double avg_grade;
} Student;
std::string date_to_string(int day, int month, int year)
{
std::string d, m, g, ret;
d = std::to_string(day);
m = std::to_string(month);
g = std::to_string(year);
ret += d;
ret += "/";
ret += m;
ret += "/";
ret += g;
return ret;
}
void get_date_from_str(int& day, int& month, int& year, std::string const& str)
{
std::stringstream ss(str);
ss >> day;
ss.ignore(1);
ss >> month;
ss.ignore(1);
ss >> year;
}
void get_numbers_from_str(std::vector<int>& vec, std::string const& str)
{
std::stringstream ss(str);
int i;
while (ss >> i)
{
vec.push_back(i);
ss.ignore(1);
}
}
double calculate_avg(std::vector<int> const& vec)
{
int sum = 0;
for (auto item : vec)
{
sum += item;
}
return (double) sum / vec.size();
}
int main()
{
std::ifstream students_file("students.txt");
std::ofstream report_file("report.txt");
if (students_file.is_open() && report_file.is_open())
{
report_file << std::setw(30) << std::left << "Student"
<< std::setw(10) << "Indeks"
<< std::setw(20) << "Datum rodjenja"
<< std::setw(10) << "Prosjek" << std::endl;
report_file << std::setw(30) << std::left << "-------"
<< std::setw(10) << "------"
<< std::setw(20) << "--------------"
<< std::setw(10) << "-------" << std::endl;
std::vector<Student> students;
std::string name;
while (std::getline(students_file, name))
{
std::string index_no;
std::string date_of_birth;
std::string grades;
Student student;
student.name = name;
std::getline(students_file, index_no);
std::stringstream(index_no) >> student.index_no;
std::getline(students_file, date_of_birth);
std::getline(students_file, grades);
get_date_from_str(student.day_of_birth, student.month_of_birth, student.year_of_birth, date_of_birth);
get_numbers_from_str(student.grades, grades);
student.avg_grade = calculate_avg(student.grades);
students.push_back(student);
}
for (auto student : students)
{
report_file << std::setw(30) << std::left << student.name
<< std::setw(10) << student.index_no
<< std::setw(20) << date_to_string(student.day_of_birth, student.month_of_birth, student.year_of_birth)
<< std::setw(10) << std::setprecision(2)
<< student.avg_grade << std::endl;
}
}
else
{
std::cout << "Unable to open one of the files!" << std::endl;
}
return 0;
}
First, I think you have complicated your code for both, reading and understanding it, while you have some C++ features that can simplify this part with signs and parsing date and grades.
I have also created struct Student and I have split some of the code into functions.
And, regarding your solution. As someone already mentioned in the comment, fault is in these lines:
while (load >> grades[i])
{
load >> sign;
average += grades[i];
i++;
}
You are 'eating' one character more than you need. And this character is the first character of the name of the second student. Therefore, if you change your code to following, you'll see what I'm talking about:
while (load >> grades[i])
{
if (i != 4) // just a temporary solution, since the first student has 5 grades
load >> sign;
average += grades[i];
i++;
}

How to pass functions defined in a class as arguments to functions?

getMessage method extracts the first letter in each word of input string.
Example:
input = "Find the first letters of this Sentence"
output = FtflotS
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
using namespace std;
class HiddenMessage {
public:
bool space (char c) {
return isspace(c);
}
bool not_space (char c) {
return !isspace (c);
}
string getMessage(string text) {
string ret;
typedef string::const_iterator iter;
iter i, j;
i = text.begin();
while (i != text.end()) {
i = find_if (i, text.end(), not_space); // error here
j = find_if (i, text.end(), space); // error here
if (i != text.end()) {
ret += *i;
}
i = j;
}
return ret;
}
};
//compiler error:
//error: invalid use of non-static member function
I tried making definitions of space and not_space static and it did
not work.
getMessage is called from the main below:
#include <ctime>
#include <cmath>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
using namespace std;
int main(int argc, char* argv[])
{
if (argc == 1)
{
cout << "Testing HiddenMessage (250.0 points)" << endl << endl;
for (int i = 0; i < 20; i++)
{
ostringstream s; s << argv[0] << " " << i;
int exitCode = system(s.str().c_str());
if (exitCode)
cout << "#" << i << ": Runtime Error" << endl;
}
int T = time(NULL)-1456061889;
double PT = T/60.0, TT = 75.0;
cout.setf(ios::fixed,ios::floatfield);
cout.precision(2);
cout << endl;
cout << "Time : " << T/60 << " minutes " << T%60 << " secs" << endl;
cout << "Score : " << 250.0*(.3+(.7*TT*TT)/(10.0*PT*PT+TT*TT)) << " points" << endl;
}
else
{
int _tc; istringstream(argv[1]) >> _tc;
HiddenMessage _obj;
string _expected, _received;
time_t _start = clock();
switch (_tc)
{
case 0:
{
string text = "compete online design event rating";
_expected = "coder";
_received = _obj.getMessage(text); break;
}
case 1:
{
string text = " c o d e r ";
_expected = "coder";
_received = _obj.getMessage(text); break;
}
case 2:
{
string text = "round elimination during onsite contest";
_expected = "redoc";
_received = _obj.getMessage(text); break;
}
case 3:
{
string text = " ";
_expected = "";
_received = _obj.getMessage(text); break;
}
/*case 4:
{
string text = ;
_expected = ;
_received = _obj.getMessage(text); break;
}*/
/*case 5:
{
string text = ;
_expected = ;
_received = _obj.getMessage(text); break;
}*/
/*case 6:
{
string text = ;
_expected = ;
_received = _obj.getMessage(text); break;
}*/
default: return 0;
}
cout.setf(ios::fixed,ios::floatfield);
cout.precision(2);
double _elapsed = (double)(clock()-_start)/CLOCKS_PER_SEC;
if (_received == _expected)
cout << "#" << _tc << ": Passed (" << _elapsed << " secs)" << endl;
else
{
cout << "#" << _tc << ": Failed (" << _elapsed << " secs)" << endl;
cout << " Expected: " << "\"" << _expected << "\"" << endl;
cout << " Received: " << "\"" << _received << "\"" << endl;
}
}
}
You have two problems.
The first, is you are supplying non-static class member functions (space and not_space) to find_if which expects a function object or pointer. So, declare them static if you want them to remain your class, or make them global by placing them outside the class.
The second, your string text parameter is non-const, but, you are working with a const interator type. begin() and end() calls will return const or non-const iterator depending on the calling object (text in this case) and whether or not it is const qualified. So, declare your text parameter as const.

c++ Script that finds questions in string

I am new at coding , been coding for about a week and i am trying to do a script that finds the "?" and the "." in the script , then outputs their position in the script and i use those value to print the question to a text file.
Except it does not really work.
If you put the value in like this, it works.
myfile << test.substr( 18, 20 )
But like this it does not work it just print the whole script from the value of dot[0] until the end of the script.
myfile << test.substr( dot[0], interrogation[0] )
The way that i use to find the "?" position in the string is also not very accurate.
Where there is the .
if(x > 0){
I had a while loop but i replaced it for debugging reasons .
This is the whole code.
If you can help me i appreciate it.
Thanks.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main (){
std::vector< int > interrogation ;
std::vector< int > dot;
string look = "?";
string look_again = ".";
string test = "ver. o que e isto? nao sei. ola? adeus. fghfghfhfghf";
string::size_type pos = test.find(look);
string::size_type sop = test.find(look_again);
string::size_type exc = test.find(look_again_again);
while (pos != std::string::npos)
{
int a = pos ;
int b = sop;
cout << " . found at : " << sop << std::endl;
cout << " ? found at : " << pos << std::endl;
interrogation.push_back(a);
dot.push_back(b);
string fragment = test.substr (0 , pos ); // works
//cout << fragment << endl ;
string fragment2 = test.substr (0 , sop ); // works
//cout << fragment2 << endl ;
pos = test.find(look, pos + 1);
sop = test.find(look_again, sop + 1);
}
int x = 1;
if(x > 0){
int a = 1;
int q = dot[a];
int w = interrogation[a];
// to save text
// to save text
string save = "saved_question.txt" ;
ofstream myfile;
myfile.open (save.c_str(), ios::app);
myfile << test.substr( 18, 20 ) + "\n" ;
myfile.close();
cout << "Question saved in text file" << endl;
}
}
The code is not finished yet but i got it working with help.
Thanks
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main (){
std::vector< int > interrogation ;
std::vector< int > dot;
//std::vector< int > exclamation;
string look = "?";
string look_again = ".";
string look_again_again = "!";
string test = " ver.o que e isto? nao sei. ola? adeus.";
string::size_type pos = test.find(look);
string::size_type sop = test.find(look_again);
while (pos != std::string::npos)
{
int a = pos ;
int b = sop;
cout << " . found at : " << sop << std::endl;
cout << " ? found at : " << pos << std::endl;
// cout << " ! found at : " << exc << std::endl;
interrogation.push_back(a);
dot.push_back(b);
//exclamation.push_back(c);
string fragment = test.substr (0 , pos ); // works
//cout << fragment << endl ;
string fragment2 = test.substr (0 , sop) ; // works
//cout << fragment2 << endl ;
string fragment3 = test.substr (dot.back() + 1, interrogation.back() - dot.back()); // works
cout << fragment3 << endl ;
pos = test.find(look, pos + 1);
sop = test.find(look_again, sop + 1);
}
}
You can do something like this:
void function(string str) {
string sentence = "";
for(int i=0; i < str.length(); i++) {
if(str[i] == '.' || str[i] == '!')
sentence = ""; // the sentence is not a question so clear the sentence
else if(str[i] == '?') {
sentence.push_back(str[i]);
cout << sentence << endl; // output the question - just replace cout with your ofstream
}
else
sentence.push_back(str[i]);
}
}
I think I've seen this question before though..

Using stringstream: Only working for first value

I am trying to use stringstream with peek() and get(n). It is only working for the first value, Name, and then gives empty values for everything except weight, which gives 0 (when printing out with cout).
stringstream ss;
ss.str("John Smith, Toyota, 160, brown blue, orange");
//Extract into these variables:
string Name = "" ;
string car = "" ;
int weight = 0;
string hairColor = "";
string eyeColor = "";
string favoriteColor = "";
while (ss.peek () != ',')
{
char temp;
ss. get(temp);
Name += temp;
}
while (ss.peek () != ',')
{
char temp;
ss. get(temp);
car += temp;
}
while (ss.peek () != ',')
{
char temp;
ss. get(temp);
weight += temp;
}
while (ss.peek () != ',')
{
char temp;
ss. get(temp);
hairColor += temp;
}
while (ss.peek () != ',')
{
char temp;
ss. get(temp);
eyeColor += temp;
}
while (ss.peek () != ',')
{
char temp;
ss. get(temp);
favoriteColor += temp;
}
cout << "Name is: " << Name << endl;
cout << "car is: " << car << endl;
cout << "weight is: " << weight << endl;
cout << "Hair color is: " << hairColor << endl;
cout << "Eye color is: " << eyeColor << endl;
cout << "Favorite color is: " << favoriteColor << endl;
What is the problem here?
The immediate problem is that after the first loop the continuation condition is false, so none of the following loops, with the same continuation condition, do anything.
However, the usual way to extract an item is to use the getline function, where you can specify an arbitrary delimiter character instead of newline.
Doing this properly is a bit involved:
#include <iomanip> // std::setw
#include <iostream>
#include <stdexcept> // std::runtime_error, std::exception
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <sstream> // std::istringstream
using namespace std;
auto fail( const string& s ) -> bool { throw runtime_error( s ); }
auto get_string( istream& stream, const char delimiter = ',' )
-> string
{
while( stream.peek() == ' ' )
{
stream.get();
}
string result;
getline( stream, result, delimiter )
|| fail( "get_string: failed to extract text" );
return result;
}
template< class Type >
auto get( istream& stream, const char delimiter = ',' )
-> Type
{
istringstream conversion_stream( get_string( stream, delimiter ) );
Type result;
conversion_stream >> result
|| fail( "get: failed to convert text to value" );
return result;
}
template<>
auto get<string>( istream& stream, const char delimiter )
-> string
{ return get_string( stream, delimiter ); }
template< class Type >
void display( const char name[], const Type& value )
{
cout << setw( 15 ) << name << " = " << value << endl;
}
#define DISPLAY( x ) display( #x, x )
void cpp_main()
{
istringstream data_stream( "John Smith, Toyota, 160, brown, blue, orange" );
const auto name = get<string>( data_stream );
const auto car = get<string>( data_stream );
const auto weight = get<int>( data_stream );
const auto hairColor = get<string>( data_stream );
const auto eyeColor = get<string>( data_stream );
const auto favoriteColor = get<string>( data_stream );
DISPLAY( name );
DISPLAY( car );
DISPLAY( weight );
DISPLAY( hairColor );
DISPLAY( eyeColor );
DISPLAY( favoriteColor );
}
auto main() -> int
{
try
{
cpp_main();
return EXIT_SUCCESS;
}
catch( const exception& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
ss.peek() == ',' becomes true at the end of the first loop. Use std::getline so you won't have this problem:
if (getline(ss, Name) && getline(ss, car) && ss >> weight && getline((ss >> std::ws).ignore(), hairColor))
{
cout << "Name is: " << Name << endl;
cout << "car is: " << car << endl;
cout << "weight is: " << weight << endl;
cout << "Hair color is: " << hairColor << endl;
cout << "Eye color is: " << eyeColor << endl;
cout << "Favorite color is: " << favoriteColor << endl;
}