My program ends before user input is allowed and output data is not saving to the csv file properly.

"Append the following records to a chosen text file.
Print all the values in the file to the screen.
Read the values from a text file, print to screen and create a csv file using the data printed on screen."

Can someone please review my program and see why my program ends before user input is allowed to be done and also why only one line is saving to the csv file.

The name of the text file is in the code with also the number of variables that would hold the data, so anyone can just make the file up.

I have a feeling it will be something related to void functions but I am still not really sure. I just need to be guided to the right direction since it's soo close to working properly.

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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  
#include <iostream>
#include <fstream>      //Used to read and write to files
#include <string>
#include <iomanip>
#include <sstream>      //Used to parse a string(break it up)
#include <cstdlib>      //Used for error checking with exit function
using namespace std;

//======================line=====================//
void line()
    {
       cout<<"\t";
       for(int i=8;i<41;i++)
       cout<<"--";

       cout<<"\n";
    }
//======================line=====================//

//add data from output to create csv file
void createCSV()
{   string first;
    string last;
    string state;
    int salesZ;
    int salesO;
    int salesT;
    int units;


    string fileCSV;
    char contin = 'y';
    cout<<"\n\n\n\tEnter CSV filename: ";
    getline(cin,fileCSV);
    ofstream outfile(fileCSV.c_str(), ios::app);
    if(!outfile){
        cout <<"\n\tERROR: Unable to open file."<<endl;
        exit(1);
    }
    cout<<endl;
    do{

        fileCSV << first << ","<< last << ","<< state << ","<< salesZ 
                   << ","<< salesO << ","<< salesT << ","<< units << endl;


        cout<<"\n\tCreate another CSV file? (y/n) ";
        cin>>contin;
        cout<<endl;
    }
    while(contin == 'y');
        outfile.close();


    }


//append data to the text file from user input
void appendtoFile()
{
    string first;
    string last;
    string state;
    int salesZ;
    int salesO;
    int salesT;
    int units;

    string fileName;
    char cont = ('y');
    cout<<"\n\n\n\tEnter filename to append: ";
    getline(cin,fileName);
    ofstream outFile(fileName.c_str(), ios::app);
    if(!outFile){
        cout <<"\n\tERROR: Unable to open file."<<endl;
        exit(1);
    }
    cout<<endl;
    do{
        cout<<"Enter First Name: ";
        cin>>first;
        outFile<<first<<endl;

        cout<<"Enter Last Name: ";
        cin>>last;
        outFile<<last<<endl;

        cout<<"Enter State: ";
        cin>>state;
        outFile<<state<<endl;

        cout<<"Enter Sales History 0: ";
        cin>>salesZ;
        outFile<<salesZ<<endl;

        cout<<"Enter Sales History 1: ";
        cin>>salesO;
        outFile<<salesO<<endl;

        cout<<"Enter Sales History 2: ";
        cin>>salesT;
        outFile<<salesT<<endl;

        cout<<"Enter Units: ";
        cin>>units;
        outFile<<units<<endl;
        cout<<endl;

        cout<<"\n\tEnter another customer record? (y/n) ";
        cin>>cont;
        cout<<endl;
    }
    while(cont == 'y');
        outFile.close();

}



int main (){
    //opening the text file
    ifstream myfile("customer.txt");
    ifstream myfile1("customer1.txt");
    ifstream myfile2("customer2.txt");

    //defined variables: this will contain the data read from the text file
    string first;
    string last;
    string state;
    int salesZ;
    int salesO;
    int salesT;
    int units;

    //======================header=====================//
    cout<<setw(13)<<"First"<<setw(15)<<"Last"<<setw(15)<<"State"
        <<setw(20)<<"Sales History"<<setw(12)<<"Units\n";
    cout<<setw(64)<<"  0    1    2"<<endl;
    //======================line=====================//
    line();
    for(int i=0;i<5;i++)
    //======================line=====================//
    //======================header=====================//

    //and output data read from the text file
    while (myfile >> first >> last >> state >> salesZ >> salesO >> salesT >> units){
        cout<<"\t"<<right<<setfill(' ')<<right<<setw(16)<<left<<first<<setw(16)<<left<<last<<setw(2)<<state
            <<right<<setw(12)<<salesZ<<setw(5)<<salesO<<setw(5)<<salesT<<setw(10)<<units<<endl;
    }

    while (myfile1 >> first >> last >> state >> salesZ >> salesO >> salesT >> units){
        cout<<"\t"<<right<<setfill(' ')<<right<<setw(16)<<left<<first<<setw(16)<<left<<last<<setw(2)<<state
            <<right<<setw(12)<<salesZ<<setw(5)<<salesO<<setw(5)<<salesT<<setw(10)<<units<<endl;
    }

    while (myfile2 >> first >> last >> state >> salesZ >> salesO >> salesT >> units){
        cout<<"\t"<<right<<setfill(' ')<<right<<setw(16)<<left<<first<<setw(16)<<left<<last<<setw(2)<<state
            <<right<<setw(12)<<salesZ<<setw(5)<<salesO<<setw(5)<<salesT<<setw(10)<<units<<endl;
    }
    //if the file is open
    if (myfile.is_open()){
    //closing the file
        myfile.close();
    }
    //if the file is not open output
    else cout <<"\n\tERROR: Unable to open file."<<endl;

    if (myfile1.is_open()){
        myfile1.close();
    }
    else cout <<"\n\tERROR: Unable to open file."<<endl;

    if (myfile2.is_open()){
        myfile2.close();
    }
    else cout <<"\n\tERROR: Unable to open file."<<endl;


    char option = ('y');
    cout<<"\n\n\n\tEnter \"y\" to append to text file or \"n\" to create CSV file: ";
    cin>>option;

    if(option == 'y'){
        appendtoFile();
    }
    else createCSV();

    cout<<endl;
    cout<<endl;

    return 0;
}
Last edited on
Hello missyredx,


see why my program ends before user input is allowed to be done


At what point do you feel that user input is not being accepted.


The name of the text file is in the code with also the number of variables that would hold the data, so anyone can just make the file up.


No. Any file I or someone would make up could be completely different from what you are using. It is better to provide the input files that you are using or at least a good sample to work with.

I also noticed that in "main" you open three file streams, but never check if they opened properly.

In your functions you open the file stream, (ofstream), and check if it is open. This in not completely necessary because if the output file does not exist it will be created. Checking if the stream opened is used for when there is a path to the file. Usually the has a problem.

Also if your compiler is set to use the C++11 standards or later the ".cstr()" is not needed a "std::string" will work fine as you used in "main".

The use of "exit()" in your functions may work, but it is best not to use "exit()" for general use. The function should return either a "bool" or an "int" back to main any you should exit the program there with a return statement if needed.

I also noticed in the "appendtoFile" function if you are creating a CSV file you have missed the ","s that separate the fields.

It will take some time to get the program loaded and create the input files, so it could be tomorrow morning before I have something.

Andy
Thank you for helping me Andy. I greatly appreciate it.

Here is how the output displays:

	First           Last          State       Sales History      Units
                                                     0    1    2
        ------------------------------------------------------------------
        Doug            Douglas         NY         100   50  300      2000
        Alicia          George          NC         250  100   80      5000
        Mary            Lambert         FL          50  100  200      1000
        John            Smith           LA         100  200  300      5000
        Charles         Holder          VA          50   80  200      1000
        Charlie         Lawrence        VA          10  800  200      3200
        Alina           Jacob           LA         200  500  300      5000
        Charlie         Lawrence        VA          10  800  200      3200
        Alina           Jacob           LA         200  500  300      5000
        Amy             Lamb            FL          50  100  200      1700
        Alina           Jacob           LA         300  600  300      9800
        Charlie         Lawrence        VA          10  800  200      3200
        Dovie           Edwards         NY         200  500  300      5000
        Alicia          George          NC         250  100   80      5000
        Mary            Lambert         FL         500  320  200      9000



        Enter "y" to append to text file or "n" to create CSV file: y



        Enter filename to append:
        ERROR: Unable to open file.

Process returned 1 (0x1)   execution time : 218.643 s
Press any key to continue.


As you can see, the program ends right after moving on to the next option.

The output data here came from all three text files but, I just did that to test my formatting. "customer.txt" is the main file, "customer1.txt" is actually the data to append to "customer.txt", and "customer2.txt" is a second file to test with creating a csv file.

I plan on removing two of the three or just replace it with a function asking the user for file name, once after the program starts working as intended. It's just there to help troubleshoot.

I think using a "bool" will be the best way to replace "exit()"

and if you mean:

do{

fileCSV << first << ","<< last << ","<< state << ","<< salesZ


fileCSV is the string variable for user data but from all the sources I've seen regarding data to csv files, I didn't see anything that was really clear on how to implement this statement.

I hope I've made things a bit clearer but will add more if needed.

missyredx
Hello missyredx,

The output data here came from all three text files but, I just did that to test my formatting. "customer.txt" is the main file, "customer1.txt" is actually the data to append to "customer.txt", and "customer2.txt" is a second file to test with creating a csv file.
But "customet1.txt" is never used to append to "customer.txt" you are using "cin" statements to get your information. I would consider another function to read the "customet1.txt" file and append it to "customer.txt"

I have not worked on the "createCSV" function yet, but at first glance you are not getting any information for your variables before you write to the file. With one exception the write statement should work. What you have is fileCSV << first << ","<< last << .... The problem is that "fileCSV" is defined as a string and the "<<"s is not the proper to create a new string. What you need here is "outfile" to write to the file.

To use "fileCSV" it would be
 
fileCSV = first + "," + last << "," + state + "," + salesZ  + "," + salesO + "," + salesT + "," +units + '\n';

Then you could write "fileCSV" to output.

I hope I've made things a bit clearer but will add more if needed.
The only part that is not clear is the "customer.txt" file. I do not know what you started with, but the "append" function writes each field on a seperate line. I created the file this way:
Doug Douglas NY 100 50 300 2000
Alicia George NC 250 100 80 5000
Mary Lambert FL 50 100 200 1000
John Smith LA 100 200 300 5000
Charles Holder VA 50 80 200 1000


That may be my choice to put one record per line if it can be done, in this case it can. That is why I wanted you to post what you are using, so that everyone would be using and seeing the same information and not changing things around.

I am going to cover the program in the next message because it is likely to be long as there is much to cover.

Andy
Hello missyredx,

Sorry this got long and I will have to do it in two parts.

There is no quick way , so I will start at the top.

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
#include <cctype> // <--- Added. For std::tolower() and std::toupper() + others. See http://www.cplusplus.com/reference/cctype/ for more details.
#include <iostream>
#include <iomanip>
#include <limits>
#include <string>
//#include <vector> // <--- Not used yet, but soon.
//#include <cstdlib>      // Used for error checking with exit function. Since you should not use "exit" there is no need for this file.

#include <fstream>      // Used to read and write to files
#include <sstream>      // Used to parse a std::string(break it up)

//using namespace std;  // <--- Best not to use.
// The most recent post that is worth reading. http://www.cplusplus.com/forum/beginner/258335/

//======================line=====================//
void line()
{
	//std::cout << '\t' << std::string(66, '-') << '\n'; // <--- An alternative.

	std::cout << "\t";

	for (int i = 8; i < 41; i++)
		std::cout << "--";

	std::cout << "\n";
}
//======================line=====================// 

In the first group of include files, and note this is my personal choice, I have found that these are the most used include files in any program. I also found, by accident, the alphabetical order helps to remind you when something is missing.

The second group of header files is specific to the program.

Eventually the third goup would be any header files that you write and use the double quotes instead of the <>s.

I included this first function to show you how you could use one line to replace what you have done with the for loop. Just an option/alternative. I would say that using the "std::string" as I have is something to get use to. It comes in very handy.

Be careful of using the '\t'. At the beginning of a "cout" it is not a problem, but farther in things may not line up the way that you want. That is where the "std::string" I used or using "std::setw()" can work better.

I have not worked on the "createCSV" function yet, but I do see problems there.

For the "appendtoFile" function:
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
bool appendtoFile()
{
	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};

	std::string fileName;
	char cont = 'y';  // <--- Changed.

	std::cout << "\n\n\n\tEnter filename to append: ";
	getline(std::cin, fileName);

	std::ofstream outFile(fileName, std::ios::app);

	if (!outFile) // <--- OK if you leave, but not really needed.
	{
		std::cout << "\n    ERROR: Unable to open file." << std::quoted(fileName) << std::endl;

		return true;
	}

	std::cout << '\n'; // <--- "endl" is not always needed. 

You are good with this part of the function.

Lines 6 - 9 I like to initialize the variables at least for the peace of mind that they do not contain garbage. The way that you are using these variables this part is optional.

In line 12 the () are not needed. It did not seem to make any difference when they were left.

In line 22 returning "true" may seem backwards, but it works and I will explain that more later.

In addition to the comment on line 26 I tend to save the "endl" for the last line to make sure the buffer is flushed.

You said

"customer1.txt" is actually the data to append to "customer.txt"


But your do/while loop does not read any file to append to "customer.txt". It just asks for user input. What you could do is create another function that reads a file then appends to a different file or create an overloaded function for this purpose. The overloaded function would have different parameters, so you could use the same name and the compiler would know which function to choose.

In the do while loop I offer this suggestion:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
do
{
	std::cout << "Enter First Name: ";
	std::cin >> first;
	outFile << first << ' '; // <--- Might even change the '\n' to ' '. This would put each field on one line and not separate lines.

	std::cout << "Enter Last Name: ";
	std::cin >> last;
	outFile << last << ' ';

	std::cout << "Enter State: ";
	std::cin >> state;
	outFile << state << ' ';

This would be the same for the rest of the inputs.

I would also suggest removing each "outFile" line and just put one after all your inputs.
 
outFile << first << ' ' << last << ' ' << state << ' ' ... << units << std::endl


And for the while condition while (std::tolower(cont) == 'y'); This saves you from writing while (cont=='y' || cont=='Y');. Here checking for both cases is needed. Do not count on a user following directions. Anticipate problems.

End part 1.
Part 2.

Now for "main".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
	const std::string inFileNameCust1{ "customer.txt" };

	//opening the text file
	std::ifstream inFileCust1(inFileNameCust1);
	//std::ifstream myfile1("customer1.txt");
	//std::ifstream myfile2("customer2.txt");

	if (!inFileCust1)
	{
		std::cout << "\n    File " << std::quoted(inFileNameCust1) << " did not open!\n";
		
		return 1;
	}

	//defined variables: this will contain the data read from the text file
	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};


You are good down to here. Some names I have changed. This is just a suggestion. You are free to use whatever name(s) you like.

Note: a "std::string" when defined is empty and does not need initialized unless you need to give it an initial value.

Lines 7 and 8 are commented out for now because I am not ready for them yet or even create these files yet.

When you open a file stream for input it is a must that you check that it opened. That would be the if statement.

The next part that prints the header and line are fine.

For this section:
1
2
3
4
5
6
7
8
9
10
11
12
13
for (int i = 0; i < 5; i++)
//======================line=====================//
	//======================header=====================//

	//and output data read from the text file
        while (inFileCust1 >> first >> last >> state >> salesZ >> salesO >> salesT >> units)
	{
		std::cout << "\t" << std::right << std::setfill(' ') << std::right << std::setw(16) << std::left << first << std::setw(16) << std::left << last << std::setw(2) << state
			<< std::right << std::setw(12) << salesZ << std::setw(5) << salesO << std::setw(5) << salesT << std::setw(10) << units << std::endl;
	}

//while (myfile1 >> first >> last >> state >> salesZ >> salesO >> salesT >> units)
//{ 

I do not understand what the for loop is for or why it is needed. When I commented it out it made no difference.

Lets explore what is happening here. The for loop starts and executes the while loop. The while loop continues until you try to read past the end of the file then it sets the "eof" bit and the while loop fails. Then you go back to check the middle condition and iterate 4 more times doing nothing because the stream is in a failed state and the while loop will not be entered.

I do not know if you have a point to the for loop, but you will have to explain that.

Also, with proper indenting, you will notice the the second and the third while loops are not part of the for loop.

After the while loops you get to these if/else statements:
1
2
3
4
5
6
7
8
//if the file is open
if (inFileCust1.is_open())
{
	//closing the file
	inFileCust1.close();
}
//if the file is not open output
else std::cout << "\n\tERROR: Unable to open file." << std::endl;

By the time you reach these you have already opened the file stream and checked it and processed the while loops above these if/else statements. So you already know that the stream is open. You do not need to check it again. And should you reach the else statement first it is to late for the error message and it is the wrong message. At this point if you want to close the stream because you are done with it then just close it.

When you get to this part of the program this is where you problems start.
1
2
3
4
5
6
7
8
9
10
11
12
char option = 'y'; // <--- You could also say "char option{ 'y' };", but I would use "char option{};". And even that is not necessary.

std::cout << "\n\n\n\tEnter \"y\" to append to text file or \"n\" to create CSV file: ";
std::cin >> option;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

if (std::tolower(option) == 'y')  // <--- Changed. That or (option == 'y' || option == 'Y').
{
	if (appendtoFile())
		return 1;
}
else createCSV();

This is one of the times that initializing a variable is not necessary because of the "cin" statement changing the variable so soon. Since you do not use "option" before the "cin" giving it a value has no use. Even if it is the same value the "cin" is basically giving it a new value.

The "cin" being formatted input the new line is left in the input buffer. So when you choose "y" and go to the "appendtoFile" function the first thing you do there is use "std::getline" which does not wait for any input, but reads the new line that is left there and moves on.

Adding line 5 after the "cin" clears the input buffer before you get to any "std::getline". As I have this line is the most portable way that any compiler and operating system can use and it has a better chance of making sure that the entire input buffer is cleared.

I hope that I did not mess up by leaving parts of the code commented out. That was to show you the difference in what you did and what I changed.

I will have to go back to take out what is not needed and shorten the code.

Last point when using a particular style pick one and be consistent in its use. Have a look at https://en.wikipedia.org/wiki/Indentation_style#Brace_placement_in_compound_statements Out of every thing presented I like the "Allman" style the best. The allignment of the {}s and indentation make it the easiest to read.

Andy
Thank you so much, Andy. It's working great so far.

What I did in order to test the program was:
(since it's not ready yet)
remove "void createCSV()" function
commented out "else createCSV();"
when entering "n" the program ends.

Due to my compiler being GNU GCC and having errors pop up, I flagged for C++11.
I added back "#include <cstdlib>"
removed "//" after "using namespace std;"
renamed "std::quoted(fileName)" to just "fileName" (had to remove all instances of "std::quoted"
added back outFile(fileName.c_str(), std::ios::app);

I used your suggestions for "void line()" and "char option{};"

This is how the program looks now (with my compiler anyway):
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <cctype> 
#include <iostream>
#include <iomanip>
#include <limits>
#include <string>
//#include <vector> // <--- Not used yet, but soon.


#include <fstream>      // Used to read and write to files
#include <sstream>      // Used to parse a std::string(break it up)
#include <cstdlib>
using namespace std;

//======================line=====================//
void line()
    {
       std::cout << '\t' << std::string(66, '-') << '\n';
    }
//======================line=====================//


//append data to the text file from user input
bool appendtoFile()
{
	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};

	std::string fileName;
	char cont = 'y';

	std::cout << "\n\n\n\tEnter filename to append: ";
	getline(std::cin, fileName);

	std::ofstream outFile(fileName.c_str(), std::ios::app);

	if (!outFile)
	{
		std::cout << "\n    ERROR: Unable to open file." << fileName << std::endl;

		return true;
	}

	std::cout << '\n'; //
    do{
        cout<<"Enter First Name: ";
        cin>>first;
        outFile << first << ' ';

        cout<<"Enter Last Name: ";
        cin>>last;
        outFile<<last<< ' ';

        cout<<"Enter State: ";
        cin>>state;
        outFile<<state<< ' ';

        cout<<"Enter Sales History 0: ";
        cin>>salesZ;
        outFile<<salesZ<< ' ';

        cout<<"Enter Sales History 1: ";
        cin>>salesO;
        outFile<<salesO<< ' ';

        cout<<"Enter Sales History 2: ";
        cin>>salesT;
        outFile<<salesT<< ' ';

        cout<<"Enter Units: ";
        cin>>units;
        outFile<<units<< ' ';
        cout<<endl;

        cout<<"\n\tEnter another customer record? (y/n) ";
        cin>>cont;
        cout<<endl;
    }
    while (std::tolower(cont) == 'y');
        outFile.close();

}



int main (){
    const std::string inFileNameCust1{ "customer.txt" };

	//opening the text file
	std::ifstream inFileCust1(inFileNameCust1);
	

	if (!inFileCust1)
	{
		std::cout << "\n    File " << inFileNameCust1 << " did not open!\n";

		return 1;
	}

	//defined variables: this will contain the data read from the text file
	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};

    //======================header=====================//
    cout<<setw(13)<<"First"<<setw(15)<<"Last"<<setw(15)<<"State"
        <<setw(20)<<"Sales History"<<setw(12)<<"Units\n";
    cout<<setw(64)<<"  0    1    2"<<endl;
    //======================line=====================//
    line();

    //======================line=====================//
    //======================header=====================//


    //and output data read from the text file
        while (inFileCust1 >> first >> last >> state >> salesZ >> salesO >> salesT >> units)
	{
		std::cout << "\t" << std::right << std::setfill(' ') << std::right << std::setw(16) << std::left << first << std::setw(16) << std::left << last << std::setw(2) << state
			<< std::right << std::setw(12) << salesZ << std::setw(5) << salesO << std::setw(5) << salesT << std::setw(10) << units << std::endl;
	}


    //if the file is open
if (inFileCust1.is_open())
{
	//closing the file
	inFileCust1.close();
}
//if the file is not open output
else std::cout << "\n\tERROR: Unable to open file." << std::endl;


    char option{};

std::cout << "\n\n\n\tEnter \"y\" to append to text file or \"n\" to create CSV file: ";
std::cin >> option;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  

if (std::tolower(option) == 'y')  
{
	if (appendtoFile())
		return 1;
}
//else createCSV();

    cout<<endl;
    cout<<endl;

    return 0;
}


I had liked your suggestion about removing each "outFile" line and just put one after all inputs, but I will try that later on for testing.

But your do/while loop does not read any file to append to "customer.txt". It just asks for user input.

Here is the assignment parameters. Hopefully it will clear things up and I do apologize for any confusion from before.

Company maintains the following information about its customers
in the customer.txt file:

Write a program to:
Part I
1) Read the data from the customer.txt file and print the values to the screen.
2) Append the following inputted records to the customer.txt file and print all the values in the file to the screen.

Part II
1) Create a new file named customer2.txt and input the following records.
2) Read the data from the customer2.txt and print the values to the screen.
3) Read the values from the customer2.txt file and create a CSV file using the data.


As I mentioned before, I plan on removing two of the three or just replace it with a function asking the user for file name. I made three files to help with troubleshooting but technically I should be starting off with just one file "customer.txt".

Thank you very, very much with explanations, it helps me greatly on improving my understanding on this assignment.

Also, I see you plan to use <vector> which is perfectly fine but if you might have any suggestions to a non-vector method (a link perhaps) I would be greatly appreciated.

missyredx
Hello missyredx,

You have made some improvements, good job, but at the same time there are parts the still need changed.

When I included the header file "<vector>" I think you misunderstood the point I was trying to emphasize. My point is that those are the most likely include files that you will use in most programs in the future. The point of the alphabetical order is to help recognize when one is missing.

This program is about files and the "vector" is not needed.

A problem I still see in "main". using your last code line 94 defines and opens a file stream. Line 97 checks if it is open and usable. So by the time you reach line 133 you know that the stream is open and you have used it to read a file. So the if statement if (inFileCust1.is_open()) is kind of pointless because you know that the file is open. You do not need to check this again just to close it. The else statement is out of place and the error message is wrong for what the if statement is doing.

Line 97 would catch any problem opening the stream, so you would never reach line 133 in the first place.

Next is that closing the file here is to early. I moved it down to before the return in "main". I will get into that shortly.

This may not have been covered yet or maybe briefly, but "main" should direct the program flow not be the program. Since you are using functions I moved parts of "main" into their own functions,

Any time you find your-self writing the same code two or more times it becomes a good candidate for a function. Two parts of your code, the headings and lines 25 - 129 I put in functions.

For the headings void PrintHeader() the only change I made there is a blank line before the function call for "line()".

The function I called PrintCustomerFile I changed it to look 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
void PrintCustomerFile(std::ifstream& inFileCust)
{
	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};

	PrintHeader();

	//and output data read from the text file
	while (inFileCust >> first >> last >> state >> salesZ >> salesO >> salesT >> units)
	{
		std::cout
			<< "\t"
			<< std::left
			<< std::setw(16) << first
			<< std::setw(16) << last
			<< std::setw(2) << state // <--- std::left still active for here, but not used because of the size of setw.
			<< std::right            // <--- std::right needed here for the numbers.
			<< std::setw(12) << salesZ
			<< std::setw(5) << salesO
			<< std::setw(5) << salesT
			<< std::setw(10) << units
			<< std::endl;
	}

	inFileCust.clear();  // <--- Resets the state bitsso that you can use the stream again.
	inFileCust.seekg(0); // <--- Moves the file pointer to the beginning of the file so you can read it again.
}

The first thing you should notice is that I am passing a file stream to the function. This way you can use the function to process more than one file.

The definition of the variables in "main" are nice, but since there are not used in "main" and no longer needed there I put them here. Also they are local to the function and destroyed when the function ends.

Next I rearranged the while loop. In your code line 127 you are doing statements in the "cout" that are not needed.

It tends to look like you do not understand some of the basics of using "setw". To start with the default setting is "std::right" and the default character for "setfil" is a space, so what you did to start the "cout" statement is basically redundant.

When you look at the way I rearranged the "cout" statement it helps. The compiler ignores the white space, but to you or someone reading the code it becomes much easier.

Since the default is "std::right" you need "std::left" before the names to put them to the left side of the block and you only need one for this and it will affect every "setw" until it is changed. So the "std::left" covers "first", "last" and "state" although "state" has a "setw" of 2, so there is no left or right side to pad with spaces.

Next is the "std::right" before you print the numbers. This will affect all the "setw"s until it is changed.

Now when writing a "cout" statement it really makes no difference if you write
std::cout << std::setw(5) << std::left << first
or
]std::cout << std::left <<std::setw(5) << first

My personal choice is the second example since the "setw" affects only the variable that follows I like to keep them as close together as I can. To me it is more logical and less confusing this way and easier when you need to make adjustments as the code is arranged.

The last part of the function lines 30 and 31 resets the state bits on the stream after the while loop has set the "eof" bit trying to read past end of the file. Line 31 moves the file pointer back to the beginning of the file so it can be used again. This works for any file stream that is sent to the function.

As a side note: The function calls to "PrintHeader" and "line" may be optimized by the compiler to inline functions because they are small in what they do. Actually you could eliminate the function "line" and just put the "cout" statement in the "PrintHeader" function as it is the only place it is used.

Since I am over half way to max size for a message I will have to cover the "append" and creat CSV functions next then "main".

Andy
Hello missyredx,

Reading the directions / instructions or what I think of as specs, (specifications) Part I #1 has always been covered. Where you have missed is Part I #2.

Given
2) Append the following inputted records to the customer.txt file and print all the values in the file to the screen.
I find this to be a bit confusing, but with all that you have said what I get is the you need to append "customer.txt" with "customer1.txt".

Your first attempt does append the file, but in the wrong way. Since the code is written I would not get rid of it just yet. It may still be useful. Maybe not for this program, but in the future.

The other thing I have a problem with is asking the user for a file name. When you see this on the screen:

                Enter filename to append :


There is no telling what will be entered.

If you are going to do this then you need to precede it a list of file names to use because any user will have no idea what can be used or they could use a file name that does not exist or misspell the file name. Then you will have a problem when you try to open the file.

It is best to write the program to avoid having the user enter a file name and the way this program is needed to be done there is no need to ask the user to enter a file name.

You have written this: std::ofstream outFile(fileName.c_str(), std::ios::app);. If you are compiling to the 2011 standards the ".cstr()" is not needed. From the 2011 standards on a "std::string" will work fine as you did in "main".

The other change I would make is:
1
2
3
4
5
6
7
8
9
10
11
std::cout << "Enter Units: ";
std::cin >> units;

std::cout << std::endl;

outFile << first << ' ' << last << ' ' << state << ' ' << salesZ << salesO << salesT << units << std::endl;

std::cout << "\n\tEnter another customer record? (y/n) ";
std::cin >> cont;
std::cout << std::endl;
	} while (std::tolower(cont) == 'y');

You only need one write to the file line. What you start with will work, but there is no need to write to the output after every input.

This is where it gets interesting.
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
bool appendtoFile(std::ifstream& inFileCust1)
{
	const std::string outFileName{ "customer.txt" };

	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};

	std::ofstream outFile(outFileName, std::ios::app);

	if (!outFile)
	{
		std::cout << "\n    ERROR: Unable to open file. \"" << outFileName << "\"" << std::endl;

		return true;
	}

	while (inFileCust1 >> first >> last >> state >> salesZ >> salesO >> salesT >> units)
	{
		outFile << first << ' ' << last << ' ' << state << ' ' << salesZ << ' ' << salesO << ' ' << salesT << ' ' << units << '\n';
	}

	return false;
}

You will notice that I have used the same name for this function with one difference the parameter. This is known as an overloaded function. As long as the parameters are different the compiler will know which function to use based on how you call it.

Line 13 is interesting. back in "main" "customer.txt" was opened as an "ifstream", but in this function you open the file as an "ofstream" This works because of scope. Line 13 is local to the function and has no knowledge of what you did in "main".

The while loop has a short task. All it needs to do is read the input file and write it to the output file. When the function ends and returns to "main" there you need to print the new "customer.txt" file.

With the instructions of:

Part II
1) Create a new file named customer2.txt and input the following records.
2) Read the data from the customer2.txt and print the values to the screen.
3) Read the values from the customer2.txt file and create a CSV file using the data.


My understanding here is for #1 you need to read the "customer.txt" and "customer1.txt" files to create the "customer2.txt" file. easy enough to do.
#2 would be to call the print function to print the "customer.txt" file to the screen.
The #3 would be to create the ".csv" file.

Now before you start writing this function you need to make some choices. First you could open "customer2.txt" as an "ofstream" create the file then close the stream and reopen it as an "ifstream". Or define it as a "fstream" and open it for both input and output. std::fstream(fileName, std::ios::in | std::ios::out);. As an "fstream" you need to tell it if it for input or output or both. Not having done this in the program I would say try the "fstream" because you have not done this yet.

Your next choice is how to write the CSV file. Do you put a "," after every field, except the last one, or do you put the "," only after the strings? For this program either way would work. How you write the file now can make a difference on how you will need to read the file later.

For what is required you can put a "," after every variable, except the last one, that would be the new line character.

The idea I have for now starts 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
bool createCSV(std::ifstream& inFileCust, std::ifstream& inFileCust1)
{
	const std::string ioFileName{ "customer2.txt" };
	const std::string outFileName{ "customer2.csv" };

	std::string first;
	std::string last;
	std::string state;
	int salesZ{};
	int salesO{};
	int salesT{};
	int units{};

	char contin = 'y';
/*
        Read inFileCust
        Write ioFile

        Read inFileCust1
        Write ioFile

        Set ioFile pointer to the beginning

        Call print function

        Read ioFile
        Create CSV file
*/
        //  End of function
}

That is my idea for now. I have partially started working on this function.

Note: "seekg()" is for the input pointer and "seekp()" is for the output pointer, but I have not had much success using "seekp".

Andy
Hello missyredx,

"main" has the biggest changes to deal with.

As I said "main" should direct the program not be the program. This is what I mean by that:
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
int main()
{
	const std::string inFileNameCust{ "customer.txt" };
	const std::string inFileNameCust1{ "customer1.txt" };

	//opening the text file
	std::ifstream inFileCust(inFileNameCust);
	std::ifstream inFileCust1(inFileNameCust1);

	if (!inFileCust)
	{
		std::cerr << "\n    File \"" << inFileNameCust << "\" did not open!\n";

		return 1;
	}

	if (!inFileCust1)
	{
		std::cerr << "\n    File \"" << inFileNameCust1 << "\" did not open!\n";

		return 2;
	}

	PrintCustomerFile(inFileCust); // <--- Covers part I step 1.

	char option{};

	std::cout << "\n\n\n\tEnter \"y\" to append to text file or \"n\" to create CSV file: ";
	std::cin >> option;
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

	//option = std::tolower(option); // <--- Alternative option. You can remove all the "std::tolower"s that follow and just leave "option = '?'"

	if (std::tolower(option) == 'y')
	{
		do
		{
			std::cout
				<< "\n"
				<< std::string(10, ' ') << "a) Append a new record.\n"
				<< std::string(10, ' ') << "b) Append a file.\n"
				<< std::string(10, ' ') << "c) Exit.\n"
				<< std::string(10, ' ') << "Enter choice: ";

			std::cin >> option;

			//option = std::tolower(option); // <--- Also needed here when you change the value of "option".

			if (std::tolower(option) == 'a')
			{
				if (appendtoFile()) // <--- Not required, but could still be useful. Since it is written do not get rid of it just use it.
					return 4;

				break;
			}
			else if (std::tolower(option) == 'b')
			{
				if (appendtoFile(inFileCust1)) // <--- Covers part I step 2
					return 5;

				PrintCustomerFile(inFileCust);

				break;
			}
			else if (std::tolower(option) == 'c')
				break;
			else
				std::cerr << '\n' << std::string(14, ' ') << "Invalid Choice! Options are (a, b or c).\n";

		} while (std::tolower(option) != 'a' && std::tolower(option) != 'b' && std::tolower(option) != 'c');
	}
	else
	{
		createCSV(inFileNameCustninFileCust1);

		}

	cout << '\n' << endl;

	//closing the file
	inFileCust.close(); // <--- Not necessary as the file will when "main" looses scope.
	inFileCust1.close();

	return 0;  // <--- Not required, but makes a good break point.
}

Lines 3 - 22 open and check the file streams, By putting the close statements at the bottom lines 81 and 82 the streams are available for use anywhere in the function.

Starting at line 36 I changed that part to make use of what you started with. It looks like this on the screen:



        First           Last          State       Sales History      Units
                                                     0    1    2
        ------------------------------------------------------------------
        Amy             Lamb            FL          50  100  200      1700
        Alina           Jacob           LA         300  600  300      9800
        Charlie         Lawrence        VA          10  800  200      3200
        Dovie           Edwards         NY         200  500  300      5000
        Alicia          George          NC         250  100   80      5000
        Mary            Lambert         FL         500  320  200      9000



        Enter "y" to append to text file or "n" to create CSV file: y

          a) Append a new record.
          b) Append a file.
          c) Exit.
          Enter choice:


This gives you a choice, but Append a file is all you need. You can keep this or change it to what you need.

I have left some work for you to do. I hope I have given you enough to figure it out.

Post the changes that you make and we can figure out anything that is not working.

I do not claim this is the best way, but it is a start and I know there will be some parts that could be improved on.

Andy
Topic archived. No new replies allowed.