Struggling to fill then print vector of structs

Howdy y'all, I am trying to take a .csv file, read it into a vector of struct. and then print out the vector. However I am unable to fill in the vector. When I check the size of the vector, it is 1, but I'm also unable to use cout << allData[0];, g++ gives the error "no match for 'operator<<'".

Below is the source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <vector>
#include <sstream>
using namespace std;

struct Entry {
	string name, gender;
	int age;
};

Entry parse_Line(ifstream &source);
int read_File(const char*);

Entry parse_Line(ifstream &source){
	string name, gender;
	int age;
	Entry tempEntry;
	
	//scan a line from the file
	source >> name >> gender >> age;
	
	//assign data to tempEntry
	tempEntry.name = name;
	tempEntry.gender = gender;
	tempEntry.age = age;

	return tempEntry;
}

int read_File(const char* fileName){
	vector <Entry> allData;
	string line;
	
	ifstream fileInput;
	fileInput.open(fileName, ios::in);
	
	if (fileInput.is_open()){
		// take each line, put it into the parse_Line function, then put it into the allData vector.
		while(getline(fileInput, line)){
			allData.push_back(parse_Line(fileInput));
		}
			
		fileInput.close();
		cout << allData[0];
		
		return 1;
	} else {
		cout << "The file name is invalid.\n";
		return 0;
	}
	
}

int main (int argc, char* argv[]) {
	//read in file name to a function using following:
	string str(argv[1]);
	
	const char* nameStr = str.c_str();
	read_File(nameStr);

	return 0;
}


And this is the error the c++ mingw compiler spits out.

1
2
   error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and '__gnu_cxx::__alloc_traits<std::allocator<Entry>, Entry>::value_type' {aka 'Entry'})
   48 |   cout << allData[0];


I'd appreciate any sort of help so thank you in advance.
Last edited on
your struct lacks a << operator, which is typically done as a friend (you can look up examples of this). Cout does not know what to do because the stream operator is not defined.

An alternative that I use for some things (mostly UI interfacing) is to allow the object to be cast to a string (provide this cast operator) so you can say cout << (string)(allData[0]);
this has pros and cons vs a stream, depending on what you need to do.

you can also just say cout << allData[0].name << " " << allData[0].gender << ... etc...

some languages do provide a default cout / stringify for your objects but c++ does not do that; its sort of pointless as the defaults almost never get the formatting you want for any nontrivial work.
Last edited on
The problem is line 47.

You are trying to cout a struct that has no operator to cout. You must define it like this...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <vector>
#include <sstream>
using namespace std;

struct Entry {
	string name, gender;
	int age;

	friend ostream& operator<<(ostream& os, const Entry& e);
};

ostream& operator<<(ostream& os, const Entry& e)
{
	os << "name: " << e.name << " gender: " << e.gender << " age: " << e.age << endl;
	return os;
}


Entry parse_Line(ifstream& source);
int read_File(const char*);

Entry parse_Line(ifstream& source) {
	string name, gender;
	int age;
	Entry tempEntry;

	//scan a line from the file
	source >> name >> gender >> age;

	//assign data to tempEntry
	tempEntry.name = name;
	tempEntry.gender = gender;
	tempEntry.age = age;

	return tempEntry;
}

int read_File(const char* fileName) {
	vector <Entry> allData;
	string line;

	ifstream fileInput;
	fileInput.open(fileName, ios::in);

	if (fileInput.is_open()) {
		// take each line, put it into the parse_Line function, then put it into the allData vector.
		while (getline(fileInput, line)) {
			allData.push_back(parse_Line(fileInput));
		}

		fileInput.close();
		cout << allData[0];

		return 1;
	}
	else {
		cout << "The file name is invalid.\n";
		return 0;
	}

}

int main(int argc, char* argv[]) {
	//read in file name to a function using following:
	string str(argv[1]);

	const char* nameStr = str.c_str();
	read_File(nameStr);

	return 0;
}
Last edited on
Before referencing argv[1], argc should be checked that it is least 2.

This code does not read/parse a csv file. It reads/parses a file with items on each line delimited by white-space characters. As coded, if the name (which starts at the line beginning) contains say a space then the rest of the line won't be read/parsed correctly. Is there any data following age on the line? Often reading/parsing a line from a file would be done by overloading operator>> for an istream and the required struct - similar to the given operator<< for ostream.

@pnwdino - would you provide a sample of input file.

Assuming that the layout of the file is:
name(nospace) gender age

then possibly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
#include <fstream>
#include <vector>

struct Entry {
	std::string name, gender;
	unsigned age {};

	friend std::ostream& operator<<(std::ostream& os, const Entry& e);
	friend std::istream& operator>>(std::istream& is, Entry& e);
};

std::ostream& operator<<(std::ostream& os, const Entry& e) {
	return os << "name: " << e.name << " gender: " << e.gender << " age: " << e.age;
}

std::istream& operator>>(std::istream& is, Entry& e) {
	return is >> e.name >> e.gender >> e.age;
}

bool read_File(const char* fileName, std::vector<Entry>& allData) {
	std::ifstream fileInput(fileName);

	if (!fileInput)
		return false;

	allData.clear();

	for (Entry e; fileInput >> e; allData.push_back(std::move(e)));
	return true;
}

int main(int argc, char* argv[]) {
	if (argc < 2)
		return (std::cout << "No file name specified\n"), 1;

	std::vector<Entry> data;

	if (!read_File(argv[1], data))
		return (std::cout << "The file name is invalid\n"), 2;

	for (const auto& d : data)
		std::cout << d << '\n';
}


Given input file:


qwery male 23
asdd female 18
zxxc male 56
lkjh female 34


displays:


name: qwery gender: male age: 23
name: asdd gender: female age: 18
name: zxxc gender: male age: 56
name: lkjh gender: female age: 34

Last edited on
Apologies, this is my input file:
abc,male,21
xyz,female,18
pqr,male,19


You're right, it is currently not reading comma delineated files. I will have to fix that, I've been so busy looking at a tree, I missed the forest.

using friend in Entry struct is working to create an output though!
OK. That is a CSV file. Replace my operator>>() function with:

1
2
3
4
5
std::istream& operator>>(std::istream& is, Entry& e) {
	std::getline(is >> std::ws, e.name, ',');
	std::getline(is >> std::ws, e.gender, ',');
	return is >> e.age;
}



name: abc gender: male age: 21
name: xyz gender: female age: 18
name: pqr gender: male age: 19

Last edited on
So I tried to apply everything to a slightly different program (in the name of learning and all that) and ran into another issue with getline. I'm trying to do the same as the original program but with a larger and more complicated csv file. The error I'm getting is:
structvect.cpp: In function 'std::istream& operator>>(std::istream&, Entry&)':
structvect.cpp:31:25: error: no matching function for call to 'getline(int&, char)'
   31 |  getline(e.pluginID, ',');


for the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <vector>
#include <sstream>
using namespace std;

struct Entry {
	string eb, ed, ee, ef, eh, ei, ej, el, ek, em, en, er, es, et, eu, ev, ew, ex, ey, ez;
	int ea, eg, ec, eo, ep, eq;
	
		friend ostream& operator<<(ostream& os, const Entry e);
		friend istream& operator>>(istream& is, Entry& e);


};

Entry parse_Line(ifstream &source);
bool read_File(const char*);
void write_File(vector <Entry>& data);

//overloading operator << and >> to be able to print out the information needed.
ostream& operator<<(ostream& os, const Entry e)
{
	os << "d: " << e.ed << " e: " << e.ee << " f: " << e.ef << " h: " << e.ei << " m: " << e.em << "\n";
	return os;
}

istream& operator>>(istream& is, Entry& e){
	getline(e.ea, ',');
	getline(is >> ws, e.eb, ',');
	getline(is >> ws, e.ec, ',');
	getline(is >> ws, e.ed, ',');
	getline(is >> ws, e.ee, ',');
	getline(is >> ws, e.ef, ',');
	getline(is >> ws, e.eg, ',');
	getline(is >> ws, e.eh, ',');
	getline(is >> ws, e.ei, ',');
	getline(is >> ws, e.ej, ',');
	getline(is >> ws, e.ek, ',');
	getline(is >> ws, e.el, ',');
	getline(is >> ws, e.em, ',');
	getline(is >> ws, e.en, ',');
	getline(is >> ws, e.eo, ',');
	getline(is >> ws, e.ep, ',');
	getline(is >> ws, e.eq, ',');
	getline(is >> ws, e.er, ',');
	getline(is >> ws, e.es, ',');
	getline(is >> ws, e.et, ',');
	getline(is >> ws, e.eu, ',');
	getline(is >> ws, e.ev, ',');
	getline(is >> ws, e.ew, ',');
	getline(is >> ws, e.ex, ',');
	getline(is >> ws, e.ey, ',');
	
	return(is >> e.ez);
}


bool read_File(const char* fileName, vector <Entry>& allData){

	string line;
	
	ifstream fileInput;
	fileInput.open(fileName, ios::in);
	
	if (fileInput.is_open()){
		// take each line, put it into the parse_Line function, then put it into the allData vector.
		for (Entry e; fileInput >> e; allData.push_back(move(e)));
			
		fileInput.close();
		//cout << allData[0];
		
		write_File(allData);
		return true;
	} else {
		return false;
	}
	
}

void write_File(vector <Entry>& data){
	for (int i=0; i<=data.size(); i++ ){
		cout << data[i] << " ";
	}
	
	return;
}

int main (int argc, char* argv[]) {
	//check for file
	if (argc < 2){
		return(cout << "No file name specified\n"),1;
	}
	//read in file name to a function using following:
	string str(argv[1]);
	vector <Entry> data;
	
	if (!read_File(argv[1], data)){
		return(cout << "That file name is invalid\n"), 2;
	}
	
	const char* nameStr = str.c_str();
	read_File(nameStr, data);

	return 0;
}


and input file
3902,string1,3,string two,string three,string 4,string five,230,string 6,string seven,string 8,string nine,stringten,string11,string12,string13,43,34,89,string 14,string 15,string 16,string 17,string eighteen,string nineteen,string twenty,string twenty one,string 22
92,b,324,c,d,e,f,g,h,i,j,k,l,m,n,43l,93403,392,r,s,t,u,v,w,x,y,z


Again, any insight would be appreciated. I've been tinkering for days.
I would first read each file line and split into a vector of string. Then you can process individual vector elements as required - eg. convert some to a number etc.

As C++20, this will split each file line into std::vector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <vector>
#include <ranges>
#include <iostream>
#include <string>
#include <fstream>

namespace rngs = std::ranges;
using namespace std::string_literals;

auto split(const std::string& str, char delm) {
	std::vector<std::string> words;

	for (const auto& word : rngs::split_view(str, delm))
			words.emplace_back(word.begin(), word.end());

	return words;
}

int main() {
	std::ifstream ifs("csv.txt");

	for (std::string line; std::getline(ifs, line);) {
		const auto& elems { split(line, ',') };

		for (const auto& s : elems)
			std::cout << s << '|';

		std::cout << "\n\n";
	}
}



3902|string1|3|string two|string three|string 4|string five|230|string 6|string seven|string 8|string nine|stringten|string11|string12|string13|43|34|89|string
14|string 15|string 16|string 17|string eighteen|string nineteen|string twenty|string twenty one|string 22|

92|b|324|c|d|e|f|g|h|i|j|k|l|m|n|43l|93403|392|r|s|t|u|v|w|x|y|z|

Topic archived. No new replies allowed.