Reading CSV file

Pages: 12
Here I have a structure WaterLog and I want to print out the contents of the CSV file in the displayLogFile_CSV function. I was told to use the getline function, now I know I messed up because when I did cout person.year it gave me a whole bunch of numbers instead of getting 2017 which is the first thing in the file.

What did I do wrong for getline?
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

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

  bool displayLogFile_CSV(fstream &file)
{
	WaterLog person; // one data record
	string sectorlog; //holds string for sector
	string input; //holds input of file as string

	if (!file)
		return false;
	else
	{
		

		//display report header
		cout << "NAME		ID		DATE		SECTOR		MONTH_1		MONTH_2		MONTH_3		TOTAL		AVERAGE" << endl;

		//until reach end-of-file, read and display each record
		while (!file.eof())
		{
			getline(file, input, ',');
			person.year = atoi(input.c_str());

			cout << person.year;
			

			
			
		}
	}
	return true;
}
Last edited on
debug: write out input.cstr() with a cout statment. what did you get, what should it have been?

also, provide us with the first line of your csv file.
This is what is in the csv file

I did output input.cstr() and it just gave me everything separated by a period


YEAR MONTH DAY NAME CUST_ID SECTOR JAN FEB MAR
2017 3 28 Franklin 000-N01 N 15.6 198.2 469.01
2017 3 28 Watson 387-W99 E 178.3 243.07 719.46
2017 3 27 Newton 297-E22 N 148.2 285.18 702.5
2017 3 28 Beelzebub 666-S77 S 2666.66 1666.66 666.66
2017 4 1 Ford 123-W99 W 234.9 709.1 856.43
2017 4 2 Cray 621-S32 S 290.7 460.67 711.47
2017 3 27 Moore 553-E17 E 310 393.1 248.36
2017 3 29 Bell 111-N02 N 38.9 157.12 100.14
2017 3 30 Marconi 201-E33 E 49 556.84 91.71
2017 3 30 Thompson 793-S39 S 199.2 508.7 397.77
so your getline correctly gets 2017, but atoi fails? Or your getline gives the whole line every time?

is this the exact input, or the program output? Where are the commas?



Last edited on
This is what my output is when I do
1
2
3
4
getline(file, input, ',');
person.year = atoi(input.c_str());

cout << input;

MONTHNAMESECTORFEB3FranklinN198.23WatsonE243.073NewtonN285.183BeelzebubS1666
.664FordW709.14CrayS460.673MooreE393.13BellN157.123MarconiE556.843ThompsonS508.7397.77Press any key to continue . . .
Last edited on
Hello ddaniel10,

If that is your "csv" file it is not a "csv" file. I would call it more a space separated file.

The "getline" is trying to find a "," to stop on and with none there it looks like it is reading the whole file.

I see in the second function that you are using "std::string"s, so why are you using C style character arrays in the first function? Just make them"std::string"s.

A true "csv" file would look like this:
2017,3,28,Franklin ,00-N01,N,15.6,198.2,469.01
ending in a new line character.

With what you have std::cin >> variableName would do a better job than "std::getline(...)".

And running the while loop condition on "!file.eof()" will process the last read twice before the while loop fails. Generally that type of while loop is done: while (getline(file, input, ',')). This way when the read fails the file stream faile and the while condition fails and is bypassed.

Hope that helps,

Andy
Hey Andy,

I'm sorry I printed the CSV file from excel, but it is comma separated.

I did talk to my instructor and I did find the error. The first line of the file is the header and I was converting the string into an integer instead of ignoring the first line. I did successfully get the person.year to print how I wanted to but when I move on to the next getline function to get person.month, it doesn't print out right and instead gives me a whole bunch of numbers again.
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
bool displayLogFile_CSV(fstream &file)
{
	WaterLog person; // one data record
	string sectorlog; //holds string for sector
	string input; //holds input of file as string
	int number;
	if (!file)
		return false;
	else
	{
		

		//display report header
		cout << "NAME" << setw(20) << "ID" << setw(15) << "DATE"
			<< setw(15) << "SECTOR"
			<< setw(15) << "MONTH_1" << setw(15) << "MONTH_2"<< setw(15) << "MONTH_3"
			<< setw(15) << "TOTAL" << setw(15) << "AVERAGE" << endl;

		//until reach end-of-file, read and display each record
		while (!file.eof())
		{
			
			getline(file, input, '\n');

			getline(file, input, ',');
			person.year = atoi(input.c_str());

			
			getline(file, input, ',');
			person.month = atoi(input.c_str());

			getline(file, input, ',');
			person.day = atoi(input.c_str());

			getline(file, input, ',');

			cout << person.month;

			
			

			
			
		}
	}
	return true;
}
Hello ddaniel10,

OK so you do have a "csv" file I will adjust what I have.

It would also help if you post the whole code with the header files and "main". It helps to see if there are any other problems that you might have.

Since you have to deal with the header line it is better to put line 23 above line 20 and just do it once not each time through the while loop.

Again while (!file.eof()) will give you two of the same on the last read which will give you a problem. The fix will be what I mentioned earlier.

I will give your new code a try after I fix the file.

Hope that helps,

Andy
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
/*
Assignment: Program #2
Purpose:	This function reads a CSV file and displays a report of the information.
*/

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
	using namespace std;

	extern bool displayLogFile_CSV(fstream &file); // this function creates a report of the data in a CSV log file

// a global data structure to hold one data record
struct WaterLog
{
	int day, month, year;    // date
	char sector;             // 'N', 'S', 'E', 'W'
	char ID[8];              // customer ID
	char name[15];           // name of customer
	double firstM, secondM, thirdM; // number of gallons used in 1st, 2nd, 3rd month of quarter
};

int main()
{
	fstream file;  // file stream

	// open a CSV file for input 
	file.open("Water_Log.csv", ios::in);

	// if file is open then display the contents
	if (file)
	{
		// display the contents
		displayLogFile_CSV(file);

		// done with the file, close it
		file.close();
	}
	else // could not open the file, tell the user
	{
		cout << "Error opening file.\n";
	}

	// pause to let user see the output
	system("pause");

	return 0;
}

/*
Function:  bool displayLogFile_CSV(fstream &file)
Parameters:
file:  a reference to a CSV log file stream
Return:
true if the file was accessed successfully, else false
Description:
This function reads a CSV water meter log file and displays its contents.
The AVERAGE column is the average water usage for the 3 month period.

SAMPLE OUTPUT:
NAME             ID         DATE         SECTOR    MONTH_1      MONTH_2   MONTH_3      TOTAL        AVERAGE
Baker            000-N01    05/28/2017   North     430.73       693.24    626.71       1750.68      583.56
Kimberly         387-W99    05/27/2017   East      822.42        98.55     60.68        981.65      327.22
*/
bool displayLogFile_CSV(fstream &file)
{
	WaterLog person; // one data record
	string sectorlog; //holds string for sector
	string input; //holds input of file as string
	int number;
	if (!file)
		return false;
	else
	{
		

		//display report header
		cout << "NAME" << setw(20) << "ID" << setw(15) << "DATE"
			<< setw(15) << "SECTOR"
			<< setw(15) << "MONTH_1" << setw(15) << "MONTH_2"<< setw(15) << "MONTH_3"
			<< setw(15) << "TOTAL" << setw(15) << "AVERAGE" << endl;

		getline(file, input, '\n'); //ignores the header of file

		//until reach end-of-file, read and display each record
		while (!file.eof())
		{
			
			getline(file, input, ',');
			person.year = atoi(input.c_str());

			
			getline(file, input, ',');
			person.month = atoi(input.c_str());

			getline(file, input, ',');
			person.day = atoi(input.c_str());

			getline(file, input, ',');

			cout << person.month;

			
			

			//average amount of water used over 3 month period
			
		}
	}
	return true;
}


Here is the code with main
Hello ddaniel10,

I tried out your new code with what I had set up to make it work.

Some changes I made:

The "atoi" I changed to "stoi" to use what is available from the string class and removed the ".c_str() as it is not needed with "stoi" and a "std::string".

I finished out the input from the "csv" file and realized that the last field in the line does not need the "," delimiter either leave it off or use "\n". Using no delimiter uses "\n" be default.

For the fields that are floating point numbers use "stod".

The variables that input strings can go directly into the struct variable like:
std::getline(file, person.name, ',');. See http://www.cplusplus.com/reference/string/ and http://www.cplusplus.com/reference/string/basic_string/ for more of what can be done with the string class.

You are almost there just some input to read the file properly and setup the output.

Hope that helps,

Andy
Hello ddaniel10,

Thank you for the whole file. I will have to look it over tomorrow to see what else I may find.

Lines 10 and 12 watch your indenting. These lined do not need to be indented.

The use of "extern" on line 12 is not needed. "extern" is when something is in another file not the same file. What you have on line 12 is the proto type of the function that follows "main". All you need is the return type, function name and the parameter(s) ending with a ";". Remove "extern" and you will have a proper proto type.

Hope that helps,

Andy
Great thank you! I will upload the updated program tomorrow.
Ok when I do put
 
getline(file, input, '\n'); //ignores the header of file 


above the while loop and ignore it and then do person.year it doesn't give me the same value, but does give me the corrct value if I do put getline(file,input, '\n') below the while loop.
Hello ddaniel10,

As I worked with your program I found:

"fstream" will include the header files "ifstream" and "ofstream" therefore in "main" you could write the line ifstream file; and then in the open statement you would not need the "ios::in" to describe it as an input file.

Including the program inside an if/else statement may work, but does not look very nice. your if statement would work better as:

1
2
3
4
5
6
7
8
if (!file)
{
    std::cout << "your error message" <<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread".
    exit(1);  // <--- Because there is no reason to continue. The (1) means there is a problem.
}

// <--- Continue with rest of program. 

"system("pause"); should be avoided. It is OK for your own personal use, but not a good choice for production code. I use this in place of "system("pause")":

1
2
3
4
5
6
// The next line may not be needed. 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;


This next part refers to the code below.

First I changed the return type to "void" because there is nothing to return. Also removed the bool from "main" because I was using old version. In your latest version line 12 has your prototype as an "extern" and it should not be an "extern".

I removed the if/else statement because by the time you reach this function the file stream should be open and checked. If it had failed you should never have reached this part. This would be different if you had opened the file in the function.

In the "cout" statement to print the header line, starting at line 11, the "setw()" must come before what it needs to control. Coming after as you have it has no effect.

On line 26 I used "stoi" from the "string" class to convert the string to an "int". Starting at line 42 I used "stod" to convert the string to a "double".

Lines 36 and 38 I used to convert the string to a "char". This could have been done with:
1
2
file >> person.sector;
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits> 

You need the second line to remove the "\n" from the input buffer before the next "getline". This also shows you how to get the first character from a string.

Line 48 is there to give you two decimal places in your output. "std::showpoint" allows you to print ".00" if needed.

Lines 52 to 71 will give you an idea of how I set up the output.

Lines 58 and 59, which work with lines 55 and 56, have an extra bit of code to add a zero to the date so it looks more uniform in the output. I had to change the "int" to a string before I could use the ternary operator which did not like adding an "int" to a string, but adding two strings together is not a problem.

Anything that you do not understand let me know.

Hope that helps,

Andy

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
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);

		std::getline(file, person.name, ',');
		std::getline(file, person.ID, ',');
		std::getline(file, sectorlog, ',');

		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;
	}
}

NAME              ID        DATE    SEC   MONTH_1   MONTH_2   MONTH_3     TOTAL     AVERAGE

Franklin       000-N01  03/03/2017   N     15.60    198.20    469.01     682.81     227.60
Watson         387-W99  03/28/2017   E    178.30    243.07    719.46    1140.83     380.28
Newton         297-E22  03/27/2017   N    148.20    285.18    702.50    1135.88     378.63
Beelzebub      666-S77  03/28/2017   S   2666.66   1666.66    666.66    4999.98    1666.66
Ford           123-W99  04/01/2017   W    234.90    709.10    856.43    1800.43     600.14
Cray           621-S32  04/02/2017   S    290.70    460.67    711.47    1462.84     487.61
Moore          553-E17  03/27/2017   E    310.00    393.10    248.36     951.46     317.15
Bell           111-N02  03/29/2017   N     38.90    157.12    100.14     296.16      98.72
Marconi        201-E33  03/30/2017   E     49.00    556.84     91.71     697.55     232.52
Thompson       793-S39  03/30/2017   S    199.20    508.70    397.77    1105.67     368.56


 Press Enter to continue
Wow thank you.

I did run the function but I get an error on line 36

" no instance of overloaded function "getline" matches the argument list"

Is there a reason why it would cause this?
Hello ddaniel10,

To answer your question of earlier. Putting the first "getline()" above the while loop as you did does get rid of the header line, but inside the while loop your first "getline()" has a third parameter of "\n" which reads the entire second line into one variable. What the third parameter should be id ",".

The next line will extract the year properly, but the next "getline()" will be reading the first field of the third line not the second field of the second line as it should.

See my last message and the code at the bottom for the proper way to read the file. It does work as you can see by the output below the code.

And remember the last field that is read does not need the third parameter or if you use one it should be "\n".

Hope that helps,

Andy
Hello ddaniel10,

I am not sure which bit of code you are referring to for line 36, so I do not know how the line is written.

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.

If you post the line of code that is giving you the problem and tell me how second parameter is defined, i.e., what type it is I can better help you. Hopefully by that time you will have figured it out yourself.

Andy
I get this error for this line, i'll try to figure this problem out.

1
2
3
std::getline(file, person.name, ',');
		std::getline(file, person.ID, ',');
		std::getline(file, sectorlog, ',');
Hello ddaniel10,

OK I do not see any problems with the lines that you have posted.

All I can think of is you said the error is on line 36, but sometimes the actual error could start on line 35 or above.

Sometimes the error messages are not as accurate as one would like to believe. It does take awhile to get use to what the error messages have to say.

It looks like these lines are inside the while loop, so show me the code from reading the first line of the file and the entire while loop and maybe I can find the problem.

Andy
Pages: 12