Array

Some automated computer-based machines can output a text file containing rows and columns of numeric data values. These data values may, for example, give a history of the operation and status of the machine while completing a certain job. Many times, technicians need to extract specific columns of numeric data from one of these files and write it to a new file. During that process, they also may want to analyze the extracted data. Building a computer program to do both of those tasks (data extraction and analysis) is the focus of this assignment.
C++ program that does the extraction and analysis described above except that the tasks shall be made somewhat simpler. The program shall read from a file "inputdata.txt" containing a name and exactly five values of floating point numbers per line in the file. The first line of the file shall contain the name of the person (first and last) that gathered the data i.e. Joe Jones. The second line of the file shall be the first row of data.
As the program reads each row of data, it shall do two things: it shall keep a running sum of the values read for that row, and shall also write the average of that row to a file "averages.txt". The program will continue reading from the file until it reaches the end of the file. This will lintel using a "while" loop and the eof() function that is talked about in Chapter 13 of the text book. The person's name is to be read in at one time using "getline" and stored under a single variable name.
Note that the program shall have no interaction with a user except for starting the program using an IDE or the command line. This means that the program shall NOT prompt the user for the entry of any data. When the program is done, it shall print to the screen the name of the person that sent the data i.e Joe Jones and the name of the output file i.e. "averages.txt".
Software Design
The design of the program consists of two function definitions: main, rowAverage.
float rowAverage(float num1, float num2,float num3,float num4,float num5)
Declare five floating point variables to hold the five numbers that are being sent each time from a row in the input file. (Note: These five variables could be declared more appropriately using an array, but we are not covering that data structure until next unit, so do not use it here.) Set up the function to add up all the values sent and save them in a local variable. Then using that variable divide by 5 and save it in another local variable. Return this value back to the "main" function that has called this function. Now it would be much easier to do all this in the "main" but we are setting things up for the topic of the next unit.
Print just the averages the output file (nothing more or nothing less). i.e.
24.63
56.1
48.9
  .
  .
  .

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
  #include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
// method signature
float rowAverage(float, float, float, float, float);
int main() {
  // local variable declared
  float n1, n2, n3, n4, n5, avg;
  string line, name;
  ifstream fin;
  ofstream fout;
  // opening the both file
  fin.open("inputdata.txt", ios::in);
  fout.open("averages.txt", ios::out);
  // checking ig file opened or not
  if (!fin || !fout) {
    cout << "Error in opening file !! exiting app \n";
    exit(-1);
  }
  // geting the name of person
  getline(fin, name);
  // using eof to read till last line
  while (!fin.eof()) {
    getline(fin, line);
    // for splitting string using sstream
    stringstream ss(line);
    string temp;
    int i = 0;
    // splitting number by space and assigning it to float variable
    while (ss >> temp) {
      ++i;
      if (i == 1)
        n1 = stoi(temp);
      if (i == 2)
        n2 = stoi(temp);
      if (i == 3)
        n3 = stoi(temp);
      if (i == 4)
        n4 = stoi(temp);
      if (i == 5)
        n5 = stoi(temp);
    }
    if (i == 5) {
      // getting the average
      avg = rowAverage(n1, n2, n3, n4, n5);
      // writing to file
      fout << avg << endl;
    }
  }
  cout << "Data sent by  : " << name << endl;
  cout << "File name : averages.txt" << endl;
  return 0;
}

float rowAverage(float n1, float n2, float n3, float n4, float n5) {
  float avg, sum;
  sum = n1 + n2 + n3 + n4 + n5;
  avg = sum / 5;
  return avg;
}//end of code 



Does this look right?
What are you really asking? Does the program work?
It's strange that your question is titled "Array" when the instructions specifically state that you are not allowed to use an array.

Does this look right?

It looks like it will execute the loop body one-too-many times. Do you notice that the last average is printed twice?

It's a common mistaken belief of beginners (and possibly your instructor) that the eof() method should be used to control an input loop. In fact it is the getline call that will first detect the end of the file condition, and you should use its return value to control the loop:

 
    while (getline(fin, line)) {

The eof() method is generally used only after the input has ended in order to determine if the end of the file was actually reached or if some other error condition stopped the input (e.g., the disk was removed).
Last edited on
Hello soccer53,

No, it does not look well and is hard to read along with some flaws in the program.

As a start if you put some blank lines in your code to break it up it becomes easier to read and easier to fine where a problem might be.

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
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

// method signature
float rowAverage(float, float, float, float, float);

int main()
{
	// local variable declared
	float n1, n2, n3, n4, n5, avg;
	string line, name;
	ifstream fin;
	ofstream fout;

	// opening the both file
	fin.open("inputdata.txt", ios::in);
	fout.open("averages.txt", ios::out);

	// checking ig file opened or not
	if (!fin || !fout)
	{
		cout << "Error in opening file !! exiting app \n";
		exit(-1);
	}

	// geting the name of person
	getline(fin, name);
	
	// using eof to read till last line
	while (!fin.eof()) {
		getline(fin, line);
		// for splitting string using sstream
	
		stringstream ss(line);
		
		string temp;
		
		int i = 0;

		// splitting number by space and assigning it to float variable
		while (ss >> temp)
		{
			++i;
			if (i == 1)
				n1 = stoi(temp);
			if (i == 2)
				n2 = stoi(temp);
			if (i == 3)
				n3 = stoi(temp);
			if (i == 4)
				n4 = stoi(temp);
			if (i == 5)
				n5 = stoi(temp);
		}

		if (i == 5)
		{
			// getting the average
			avg = rowAverage(n1, n2, n3, n4, n5);
			// writing to file
			fout << avg << endl;
		}
	}

	cout << "Data sent by  : " << name << endl;
	cout << "File name : averages.txt" << endl;

	return 0;
}

float rowAverage(float n1, float n2, float n3, float n4, float n5)
{
	float avg, sum;

	sum = n1 + n2 + n3 + n4 + n5;

	avg = sum / 5;
	
	return avg;
}//end of code 

It is also helpful to break up the instructions into smaller pieces so that they are easier to follow.

Referring to the above code it is best not to use line 6 as it WILL get you in trouble some day. Better to learn what is in the standard namespace and how to qualify it now slowly than all at once in the future.

Line 8. "method signature" works, but I would just use "proto types". It is more accurate.

Line 14. The instructions show the use of "float", but "double" is the preferred type these days and it is more accurate and will store the number better. Keep this in mind for the future.

Lines 20 and 21. You have defined "fin" and "fout" as an "istream" and "ostream". Therefore the "ios::in" and "ios::out" are not needed here. "ios::in" and "ios::out" are only needed when you define "fin" and "fout" as a "fstream" . As an "fstream" you have to tell it what type of file it is.

Line 24. This single if statement is better made into two separate if statements and better written this way:
1
2
3
4
5
6
7
// checking if file opened or not
if (!fin)
{
	cout << "Error in opening input file !! exiting app \n";
	std::this_thread::sleep_for(std::chrono::seconds(5));  // Requires header files "chrono" and "thread"
	exit(1);
}
The second line puts a pause in the program before it ends because the are times when the program ends the window it was running in will close and you will not be able to read the message. Notice I changed the "exit" number form "-1" to "1". Zero means that the program ended normally and any number greater than 1 means that there is a problem. Having more than one "exit" statement in a program the numbers greater than zero can help you track down whhere the problem is. Then I would create a second if statement for "fout". This way you will know which file stream is the problem and not have to guess.

Line 31. Is what you need to make the while condition wor properly.

Line 35. Reads the numbers form the file, but after that the string stream is not needed. Line 35 could easily be written as: fin >>n1 >>n2 >>n3 >>n4 >>n5; and you could avoid using the string stream.

Line 45. The inner while loop to process the string stream is not need and appears to be the most difficult way to process this.

Line 60. The "if" statement is not need as with the above while loop the it is connected to. You just need to call the function and write to the output file here.

Sorry I missed putting in a blank line at line 64.

After you write to the output file at line 65 this would be a good place to add to your "sum" not in the function which I will explain later.

In order to use "!eof()" in the while condition the last line of code in the while loop needs to be reading the next name before checking for "eof()".

Line 75. The function definition is correct and inside the function works

On line 81. The 5 will be promoted to a float for the calculation, but it would be better to write that as 5.0. Just a small thing and not really important, but a more proper way of writing the formula.

The program did compile for me, but I still need to check it.

With out the input file I do not know if some of my suggestions will work. It is just a guess at this point. It would be helpful if you would post the input file so everyone can work with the same information without guessing.

I still need to reread the instructions and see how they match the program. When I have done this I will let you know what I find.

Hope that helps,

Andy
Hello soccer53,

In working on the program I forgot to mention on thing.

Following line 35 you may need the line
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // <--- Requires header file <limits>. This will clear the input buffer before the next "std::getline(...);" is used.

"fin" can be changed to "std::cin" when working with the keyboard.

Andy
Last edited on
Hello soccer53,

I do not think I properly explained this use of "!fin.eof()" in the while condition.

tpb's suggestion of how to use the while condition is the more preferred way of using a while loop.

Checking for "eof" in a while condition is a problem because you will not set the "eof" bit until you try to read past the end of file. This generally will process the last read twice because there is nothing inside the while loop to detect "eof" so ti processes the last read even though there is no new information.

So to use while (!fin.eof()) the first read has to be before the while loop and the last thing you do in the while loop is read the next name. This way if you read past end of file the "eof" bit is set and when you return to the top of the while loop and check for "eof" it will work properly.

I did have a chance to test your program and found the the string stream is not needed with the changes I suggested. Following the fin >>n1 >>...; you will need to use the "fin.ignore(...);" before the next "std::getline(..)", usually put after the "std::cin >>" or "fin >>" otherwise before the net "getline()" is used, otherwise the getline will read the new line that is left in the buffer from the "fin >>" and continue on. Once this new line is read the getline will continue on until "fin >>n1 >>...;" sets the "eof" bit and the while loop can end, but ton after trying to process the information that should have been read, so it uses what was read last giving you one extra line that you do not need.

In the line fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // <--- Requires header file <limits>. the part std::numeric_limits<std::streamsize>::max() is just a way of creating a large number based on the computer and header files that are used. The '\n' that is used is the character that it is looking for to call an end to the ignore. In general the two parameters say to ignore the number of characters described in the first parameter or the character in the second parameter which ever comes first.

For the other line std::this_thread::sleep_for(std::chrono::seconds(5)); // Requires header files "chrono" and "thread" . The only thing to understand here is the number in the ()s that follows "seconds". This is the number of whole seconds that the code will wain before continuing with the next line. Even I do not understand the whole line of code, but I do know how to use it. I do realize that both of these lines of code may not be acceptable to your teacher, but the do serve a purpose and if you can explain them correctly you should be able to use them. Class is not the only place to learn. It will not teach you what most people will use daily. You need to learn outside of class and anywhere you can. The people here will help you understand any questions that you have.

Hope that helps,

Andy
Hello soccer53,

You should get in the habit of closing the file stream(s) before the program ends.For what you have this is best done before the final return statement of the program.

Do not count on the program ending to close the files.

Andy
Hello soccer53,

I had a thought that if using the "fin.ignore(...)" is a problem then this would be an alternative:

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
// geting the name of person
std::getline(fin, name);
	
// using eof to read till last line
while (!fin.eof())
{
	getline(fin, line);
	// for splitting string using sstream
	
	std::istringstream ss(line);
		
	ss >> n1 >> n2 >> n3 >> n4 >> n5;

	// getting the average
	avg = rowAverage(n1, n2, n3, n4, n5);

	// writing to file
	fout << avg << std::endl;
	//}

	// <--- Read the next name, also testing for end of file before returning to the while condition.
	std::getline(fin, line);

	//std::cout << std::endl;  // <--- Used fo testing.
}

Notice line 10 where I used "istringstream" in place of "stringstream". Although "stringstream" worked "std::istringstream" is more proper. There is "std::ostringstream" for using with output.

Since the "stringstream" is a completely different stream than "fin" this will not bother the use of "std::getline (...);" for reading the input file.

One other thing I wanted to mention. when you define a variable you should initialize that variable. This is not always necessary, but a good idea and sometimes must be done so yo do not use a variable before it receives a value. Defining a variable and leaving it uninitialized means that it contains whatever was in the memory when the compiler set aside space for thet variable. This is generally referred to as garbage.

The simplest way to intialize a variable is to use the uniform initializer of {}s. Empty {}s will initialize "int"s to zero "float"s and "double"s to 0.0 and a "char" will receive '\0'. When you learn about arrays the empty {}s will initialize each element of the array to zero. A "std"string", "std::vector" and some others are defined as empty containers to start with and do not need initialized.

That should give you something to work on for awhile.

Any questions let me know.

Hope that helps,

Andy
Topic archived. No new replies allowed.