Reading CSV file

Pages: 12
Hello ddaniel10,

I was thinking back to the struct for the "name" and "ID". If these are still defined as a C style character array this would be your problem because the second parameter of "std::getline()" has to be a "std::string" and "person.name" is not defined as a "std::string in the struct.

Changing the struct from "char name[15];" to "std::string name;" will solve the problem as it did for me. The same is true for the other character arrays in the struct.

Andy
Hey Andy,
I did talk to my instructor and he said to copy the string into an array of characters for person.name

and then he said that person.name isn't a string but an array of char, and that i'd have to use a for loop

I tried looking up how to do it, but when testing it doesn't work.
Last edited on
no, you don't need a for loop.
see other thread.

it didnt work because syntax :P
All I can say is that "std::getline(...)" only works with strings and that it is possible the the second parameter is not a "std::string" and that is what is giving you the error.

There is an overload of the getline() function that works with C-strings.

http://www.cplusplus.com/reference/istream/istream/getline/

@jlb,

Yes i thought of that after I sent the message, but could not say anything until I returned. Unless the character array is a requirement a "std::string" would work better.

I did not have a chance to check before, but the string class has the member function "copy" as in "str.copy()" that could be used since he could still "input" with "std::getline()" and then use "input.copy()" to copy to the character array.

With out more information on what the program needs to use and how it should work it is hard to know which way to go.

Andy
Unless the character array is a requirement a "std::string" would work better.

It appears to be a requirement:

I did talk to my instructor and he said to copy the string into an array of characters for person.name

and then he said that person.name isn't a string but an array of char,


but the string class has the member function "copy" as in "str.copy()" that could be used

Why waste the time copying when you can just retrieve the variable into the correct type in the first place?

Edit: Also be aware that the str.copy() function doesn't properly terminate the C-string, you need to be sure to manually do that after the copy, another potential source of errors. /Edit.

I do agree that using a std::string instead of the error prone C-string would be a better idea however since the instructor seems to be a "C-boy" keeping the C-string is probably the only alternative.



Last edited on
Hello ddaniel10,

I promised you some code and here it is. I am sorry it took so long to finish and this is what I have come up with:

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <iostream>
#include <iomanip>
#include <string>
#include <cctype>
#include <vector>
#include <fstream>
#include <chrono>
#include <thread>
#include <limits>

#pragma warning(disable : 4996)

constexpr size_t MAXNAMESIZE{ 15 };
constexpr size_t MAXIDSIZE{ 8 };

struct WaterLog
{
	int day{}, month{}, year{};    // date
	char sector{};             // 'N', 'S', 'E', 'W'
	char ID[MAXIDSIZE];              // customer ID
	char name[MAXNAMESIZE];           // name of customer	double firstM{}, secondM{}, thirdM{};
	double firstM, secondM, thirdM; // number of gallons used in 1st, 2nd, 3rd month of quarter
};

void DisplayLogFile_CSV(std::ifstream& file)
{
	WaterLog person; // one data record
	std::string sectorlog; //holds string for sector
	std::string input; //holds input of file as string
	std::string month, day;
	//int number;
	double avg{}, total{};

	//display report header
	std::cout << std::left << std::setw(15) << "  NAME"
		<< std::setw(12) << "  ID"  // <--- Sometimes adding spaces is easier to start with to get everything to line up. "stew" can be changed later.
		<< std::setw(9) << "DATE"
		<< std::setw(6) << "SEC"
		<< std::setw(10) << "MONTH_1"
		<< std::setw(10) << "MONTH_2"
		<< std::setw(10) << "MONTH_3"
		<< std::setw(10) << " TOTAL"
		<< std::setw(10) << " AVERAGE" << std::endl;

	std::getline(file, input, '\n');
	std::cout << std::endl;
	//until reach end-of-file, read and display each record
	while (std::getline(file, input, ','))
	{
		person.year = stoi(input);

		std::getline(file, input, ',');
		person.month = stoi(input);

		std::getline(file, input, ',');
		person.day = stoi(input);

		// <--- The simplest way to do this. 
		//file.getline(person.name, MAXNAMESIZE, ',');
		//file.getline(person.ID, MAXIDSIZE, ',');

		//std::getline(file, input, ',');
		//input.copy(person.name, MAXNAMESIZE, 0);
		//person.name[input.size()] = '\0';

		// <--- Using the copy member function of "string" class.
		std::getline(file, input, ',');
		size_t length = input.copy(person.name, MAXNAMESIZE -1, 0);
		person.name[length] = '\0';

		std::getline(file, input, ',');
		input.copy(person.ID, 8, 0);
		person.ID[input.size()] = '\0';

		file >> person.sector;  // <--- Changed to this because it was simpler, but needs the next line to proceed.
		file.ignore(1, '\n');  // <--- Requires header file <limits>.

		//person.sector = sectorlog[0];

		std::getline(file, input, ',');

		person.firstM = stod(input);
		std::getline(file, input, ',');
		person.secondM = stod(input);
		std::getline(file, input);
		person.thirdM = stod(input);

		std::cout << std::fixed << std::showpoint << std::setprecision(2);  // <--- Sets the floating point numbers to output two decimal places.

		std::cout << std::left << std::setw(15) << person.name;  // <--- "std::left" to put the strings to the left side.
		std::cout << std::setw(8) << person.ID << ' ';

		month = std::to_string(person.month);  // <--- Changes the "int" to a string for the next "cout" to work.
		day = std::to_string(person.day);  // <--- Changes the "int" to a string for the next "cout" to work.

		std::cout << (person.month < 10 ? "0" + month : month) << '/'
				  << (person.day < 10 ? "0" + day : day)
				  << '/' << person.year;

		std::cout << std::right << std::setw(3) << ' ' << person.sector;  // <--- "std::right" to put the numbers to the right so the decimal points line up.

		std::cout << std::setw(10) << person.firstM
				  << std::setw(10) << person.secondM
				  << std::setw(10) << person.thirdM << " ";

		avg = (person.firstM + person.secondM + person.thirdM) / 3;
		total = person.firstM + person.secondM + person.thirdM;

		std::cout << std::setw(10) << total << ' ' << std::setw(10) << avg << std::endl;
	}
}

int main()
{
	//bool result{ false };

	std::string iFileName{ "Data.csv" };

	std::ifstream inFile;

	inFile.open(iFileName);

	if (inFile.is_open())
	{
		std::cout << "\n File " << iFileName << " is open\n" << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(0));  // <--- Needs header files chrono" and "thread".
	}
	else
	{
		std::cout << "\n File " << iFileName << " did not open\n" << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread".
		exit(1);
	}

	DisplayLogFile_CSV(inFile);

	// The next line may not be needid. If you have to press enter to see the prompt it is not needed.
	//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
	std::cout << "\n\n Press Enter to continue";
	std::cin.get();

	return 0;
}


Be sure to look for the comments in the code.

If there is anything that you do not understand let me know.

Hope that helps,

Andy
Andy, nice but could be easily simplified.

Also you should avoid using exit in a C++ program, this C function doesn't understand a lot of the features of C++ and could, in severe circumstances, cause data corruption and since you're using it in main() a simple "return 1" is all you need.

A simple example of possible simplification, in main().

1
2
3
4
5
6
7
8
9
10
...
        // Your hard coding the file name so just use that hard coded value here, no need for the string.
	std::ifstream inFile("Data.csv"); // Prefer using constructors when possible.

	if (!inFile)
	{
		std::cerr << "\n The input file failed to open! " << " did not open.\n";
		return0(1);
	}
...


@jlb,

Thank you for the information.

I did not realize the "exit" is such a problem. I always thought it was a good choice for the situation it is used in.

With your input I will now start using and promoting the use of "return(1)".

Thanks again,

Andy
thank you for sharing this code :)
Thank you for the information.

You're welcome, and don't forget that your "read" function can also be greatly simplified. No real need for all of those stoi(), stod(), etc.

Topic archived. No new replies allowed.
Pages: 12