Working with arrays and text files

Consider the following text file:
=========
25
April Brown$ 73 76 88 61
Gary McLean$ 53 57 80 76
Moses white$ 67 69 78
Gayle P. Lloyd$ 81 77 91
Beatrice George$ 89 78 99
Thomas M. Williams$ 77 89 95
Orlando S. Smith$ 56 34 76 45
Eric O. Mason$ 98 91 87
Jerry K. Kitchens$ 77 59 69 77
Kendra Moore$ 96 98 89
Patrick Riley$ 97 94 88
Coral Gibson$ 77 67 81
Gordon Brown$ 73 76 58 61
D.Q.McLean$ 63 67 70 79
Sharon white$ 68 79 78
Blithe P. Lloyd$ 51 67 71
Beatrice George$ 89 78 99
Amos M. Williams$ 57 69 75
Rose S. Smith$ 66 54 76 45
Praise R. Mason$ 88 81 67
Arie E. Kitchens$ 67 59 69 77
Sandra Moore$ 63 68 69 75
Patricia Riley$ 87 74 88
Harris Peterson$ 67 77 81 79
Alice Winker$ 65 44 77 72
========================
The pseudo student name is terminated by a '$' sign or symbol.
I have a skeleton program that suppose to do the following
====================
The file contains firstly the number of students in
the class followed by, for each student, the student’s name and
the students percentage marks in each of four subjects. The
student’s name always begins with a letter and can contain any
character, it is terminated by a dollar sign.
=============================
I need help with the following functions
int open_file(ifstream&);
void sort(string[], int[][SUBJECTS], float[], int);
void output_marks(string[], int[][SUBJECTS], float[], int);

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
  #include <iostream.h>
#include <fstream.h>
#include <ctype.h>
#include <string.h>
#include <iomanip.h>
const int STRING_SIZE = 30,
STUDENTS = 100,
SUBJECTS = 4,
FALSE = 0,
TRUE = 1;
typedef char string[STRING_SIZE];

//Function Prototypes
int open_file(ifstream&);
void read_name(ifstream& ,string);
void get_studentGrades(ifstream&, int[][SUBJECTS], int);
float calc_average(int[][SUBJECTS], int);
void sort(string[], int[][SUBJECTS], float[], int);
void output_marks(string[], int[][SUBJECTS], float[], int);

int main()
{
	int i;
	int ns; // number of students
	ifstream results;
	string name[STUDENTS]; // student names
	float average[STUDENTS]; // student average marks
	int marks[STUDENTS][SUBJECTS]; // student marks
	if (open_file(results))
	{
		results >> ns;
		for (i=0; i<ns; i++)
		{
			read_name(results,name[i]);
			get_studentGrades(results,marks,i);
			average[i] = calc_average(marks,i);
		}
		output_marks(name,marks,average,ns);
		sort(name,marks,average,ns);
		output_marks(name,marks,average,ns);
	}
}
/*** Function name: open_file
	If successful returns true else false.
	Returns: int
***/
int open_file(ifstream&) {
}

/**Function name: get_studentGrades
   Output parameter - array marks
***/
void get_studentGrades(ifstream& ins, int m[][SUBJECTS], int sno) {
	// Enters SUBJECTS marks from the file ins
	// and places them in the row with index
	// sno in the array m
	int i;
	for (i=0; i<SUBJECTS; i++)
		ins >> m[sno][i];
}
float calc_average(int m[][SUBJECTS], int i) {
	// Returns the average of the marks in the
	// row with index i of the array m.

	int j;
	float sum = 0.0, avg;
	for (j=0; j<SUBJECTS; j++) {
		sum += m[i][j];
    }
    avg = sum/SUBJECTS
	return avg;
}
/**Function name: output_marks
   Operation: Outputs the tabulated marks for a class.
   Parameters: Input parameter - an array of names
   Input parameter - an array of marks
   Input parameter - an array of average marks
   Input parameter - number of students
 **/
void output_marks(string[], int[][SUBJECTS], float[], int) {
	 
 }
void sort(string[], int[][SUBJECTS], float[], int n) {
	 //string[] == name of student
	 //int[][SUBJECTS] = marks or grades
	 //float[] = average
	 //int n = number of student (25) in the file
	 
	 
 }


I was trying this function which accepts 2 parameters but not sure how to get it to work for four parameters.

/** Try out function with two parameters
void sort(string[], int n) {
char* temp, names;
// sorting - bubble sort; could be any sort algorithm
for (int i = 0; i< n -1; i++)
for(int j = 0; j< n - i - 1; j++)
if(strcmp(names[j], names[j + 1]) > 0) {
temp = (char*) calloc(30, sizeof(char));
strcp(temp, names[j]);
strcp(names[j], names[j + 1]);
strcp(names[j + 1], temp);
}
}
}
**/
======
Any help will be appreciated.

Thank you.
Last edited on
Hello daybreak528,

You did not mention what IDE/compiler you are using or how old it is, i.e., version number.

The header includes lead me to believe that what you are using is very old because "iostream.h", "fstream.h" and "iomanip.h" are pre-1998 C++ standards and my IDE/compiler does not support these header files.

It would be better if you can use an IDE/compiler that supports the 2011 standards and the 2014 standards would be even better.

Lines 6 - 10 are OK as global variables because the "const" means that they can not be changed.

Line 11 should be avoided as a global variable as any line of code that follows can change this variable and when it goes wrong it is harder to track down where it went wrong. Better to define this in "main" where you have more control of it.

The prototypes are OK, but I find that adding the variable name in the prototype is helpful. And newer IDEs will pick up on this and give helpful hints when writing the function call. I tend to write the function definition then copy it and paste it in for the prototype adding the semi-colon at the end or I write the prototype as I would function definition and copy everything up to the semi-colon and paste that as the function definition follower by the {}s to define the block of the function.

As an example this is how I changed "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
25
26
27
28
29
30
31
32
33
int main()
{
    int i;
    int ns; // number of students
    std::ifstream inFile;  // <--- Changed.
    std::string name[STUDENTS]; // student names
    double average[STUDENTS]; // student average marks
    int marks[STUDENTS][SUBJECTS]; // student marks

    if (open_file(inFile))
    {
        inFile >> ns;

        inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

        for (i = 0; i < ns; i++)
        {
            read_name(inFile, name[i]);

            get_studentGrades(inFile, marks, i);

            average[i] = calc_average(marks, i);
        }

        output_marks(name, marks, average, ns);

        sort(name, marks, average, ns);

        output_marks(name, marks, average, ns);
    }

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

First notice how I used the blank lines to break up the code and make it easier to read. The first benefit is to you the second is to anyone who has to read your code. Now the compiler will ignore white space, blank lines and comments. These are not part of the compiled code.

Line 3. Unless you need this variable outside the for loop it is better to define it inside the for loop. E.X.,
for (int i = 0; i < ns; i++). And yes the spaces in the line do help.

Line 4. "ns" does work a bit, but "numStudents" ot "numStuds" is more descriptive and easier to understand and follow in the program.

Line 5. "results" may work, but it is a bit misleading. Tends to make one think that you are storing the result of a calculation not defining a file stream. I like using the names "inFile" and "outFile", but that is up to you. Remember the point is to make this easy to understand.

Line 7. In the more modern versions of C++ "double" is the preferred floating point type, but if you ere stuck with what you have to use "float" may be the only type that you can use.

I believe that it is considered poor practice to run your program with an if or if/else statement. What you should do is:
1
2
3
4
if (!open_file(inFile))
{
    return 1;
}

This way when the function returns "false" meaning that the open did not work the (!) will change the "false" to "true" and you will leave the program to fix the problem. Therefor you are not using the if statement to drive the program.

You did not say if there are parts of the program that you have to follow, so I changed this function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*** Function name: open_file
    If successful returns true else false.
    Returns: int
***/
bool open_file(std::ifstream& inFile)
{
    const std::string inFileName{ "Students1.txt" };

    inFile.open(inFileName);

    if (!inFile)
    {
	std::cout << "\n File " << std::quoted(inFileName) << " did not open!" << std::endl;

	return false;
    }

    return true;
}


Based on the comment I changed the return value of the function, but if you are required to use a return value of "int" that is OK it should still work.

In the newer versions of C++, i.e., newer standards, "false" and "true" are defined as (0) and (1) respectively. Meaning that you do not have to define your own versions.

If the file did not open it returns 'false" back to "main" otherwise it returns "true".

Line 12. Is formatted input meaning that the new line (\n)is left in the input buffer.

Line 14. Clears the input buffer of anything that may be left. I had to do this because of the way I wrote the "read_name" function that you did not have. Using the unformatted "std::getline" the input buffer needs to be clear or it could be a problem. In this case it added the name being read to the "\n" already in the input buffer before storing it in the array when the function returns.

For the "read_name" function I started with this:
void read_name(std::ifstream& inFile, std::string& name ). For a function definition you not only need the variables type in the parameters, but you also need the variables name. For the "string" I passed it by reference, which is the best way to pass a string, also because if you just said "std::string name" this would be a local variable the would be destroyed when the function ends. Passing by reference will change he variable that was passed to the function even if it comes from an array.

The function: get_studentGrades(std::ifstream& ins, ...) is OK as is, but you should define "i" in the for loop not outside if. The part in bold is what I want to emphasize here. Mostly because that is what I am use to using and you may not be or have not learned this yet because of what you are using is so old.

First work on getting the "open_file" to work followed be reading the name and grades for at least 1 line then work on the rest. You may want to comment the function that you are not ready to work on and test and just concentrate on reading the file and filling the array first.

Andy
Hello daybreak528,

I found a problem I am not sure if you are aware of:

25
April Brown$        73 76 88 61
Gary McLean$        53 57 80 76
Moses white$        67 69 78
Gayle P. Lloyd$     81 77 91
Beatrice George$    89 78 99
Thomas M. Williams$ 77 89 95
Orlando S. Smith$   56 34 76 45


As you can see some names only have 3 grades while others have 4. This creates a problem reading the file and I am not sure what you can use.

Using a string stream would solve the problem if you can use it.

Or you may have to add a forth grade to all those who only have 3.

Andy
Hi Andy,

Thank you for taking a look at my post. I truly appreciate your efforts. For IDE I am using Geany version 1.36 Using GTK+ v2.24.32 and GLib v2.60.6 runtime libraries. OS is Windows 10 Professional.

I will fix the input file and incorporate your comments and suggestions.

Thank you.
Topic archived. No new replies allowed.