A few preliminary matters. The function signature for main()
should be int main(void)
, int main(int argc, char **argv)
, or int main(int argc, char *argv[])
.
When you open a file, it is always necessary to check for failure. fopen()
returns a null pointer when it fails, so you can check for this, and handle the error appropriately.
When reading the contents of a file in a loop, it is almost always a mistake to use feof()
to control the loop. This is because feof()
checks the end-of-file pointer, which is itself set only when an IO operation fails. In the case of the posted code, the loop is entered, and if there is nothing to read, the singleLine
buffer contents remain unchanged, doubling the final line of the text file.
The simplest way to solve your problem is to take as given that each line contains 12 grades, and use sscanf()
to parse each line of input as it is retrieved. This is not an elegant soulution:
#include <stdio.h>
#include <stdlib.h>
#define MAX_STUDENTS 100
int main(void)
{
/* Open a file */
FILE *fPointer = fopen("hello.xls", "r");
/* Make sure file opened successfully */
if (fPointer == NULL) {
fprintf(stderr, "Unable to open file\n");
exit(EXIT_FAILURE);
}
char singleLine[500];
double studentGPA[MAX_STUDENTS];
int grade[12];
size_t studentIndex = 0;
/* Loop to fetch data and calculate GPAs */
while ((fgets(singleLine, sizeof singleLine, fPointer)) != NULL &&
studentIndex < MAX_STUDENTS) {
if (sscanf(singleLine, "%d %d %d %d %d %d %d %d %d %d %d %d",
&grade[0], &grade[1], &grade[2], &grade[3],
&grade[4], &grade[5], &grade[6], &grade[7],
&grade[8], &grade[9], &grade[10], &grade[11]) != 12) {
fprintf(stderr, "Incorrect number of grades on line %zu\n",
studentIndex);
exit(EXIT_FAILURE);
}
int sum = 0;
for (size_t i = 0; i < 12; i++) {
sum += grade[i];
}
studentGPA[studentIndex] = sum / 12.0;
++studentIndex;
}
/* Display GPAs */
for (size_t i = 0; i < studentIndex; i++) {
printf("Student %zu GPA: %.2f\n", i, studentGPA[i]);
}
return 0;
}
Note that the return value of fgets()
is checked to control the file read loop; when a null pointer is returned, or when the maximum number of students has been reached, the loop terminates. This code allows more than 10 students, but requires 12 grades per student. If a line of student grades does not contain 12 entries, the program prints an error message and exits. This is done by checking the return value from sscanf()
, which is the number of successful conversions made. After each student's grades have been read, the average is calculated and stored in an array of studentGPA
s.
I have assumed integer grade entries, as suggested in the question post, but the GPAs are calculated with floating point values. Also note that I have assumed that the grades are separated by whitespace. If the grades are separated by commas (or other delimiters) the format string will need to be modified accordingly.
A more elegant approach uses strtok()
to break the lines of grades into individual tokens, based on a string of delimiters. These delimiters, delims = " ,\r\n"
, indicate that strtok()
should break the line into tokens where spaces, commas, or newlines occur. Other delimiters can simply be added to the string if needed.
Each line of grades is fetched, and strtok()
is called a first time. If there is a token, it is converted to an int
by atoi()
, and added to sum
. numGrades
is updated, and strtok()
is called again for a new token. When all grades have been read from a line, the average is calculated and stored in the studentGPA
array. Note that the denominator in the average calculation is multiplied by 1.0
to force the calculation to be double
. Also note that a more robust code would use strtol()
instead of atoi()
, checking the conversion for errors.
With this version, any number of students up to MAX_STUDENTS
can be evaluated, with differing numbers of grades.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STUDENTS 100
int main(void)
{
/* Open a file */
FILE *fPointer = fopen("hello.xls", "r");
/* Make sure file opened successfully */
if (fPointer == NULL) {
fprintf(stderr, "Unable to open file\n");
exit(EXIT_FAILURE);
}
char singleLine[500];
double studentGPA[MAX_STUDENTS];
size_t studentIndex = 0;
char *token;
char *delims = " ,\r\n";
/* Loop to fetch data and calculate GPAs */
while ((fgets(singleLine, sizeof singleLine, fPointer)) != NULL &&
studentIndex < MAX_STUDENTS) {
int sum = 0;
int numGrades = 0;
/* Get grade tokens */
token = strtok(singleLine, delims);
while (token) {
sum += atoi(token);
++numGrades;
token = strtok(NULL, delims);
}
/* Calculate student GPA */
studentGPA[studentIndex] = sum / (1.0 * numGrades);
++studentIndex;
}
/* Display GPAs */
for (size_t i = 0; i < studentIndex; i++) {
printf("Student %zu GPA: %.2f\n", i, studentGPA[i]);
}
return 0;
}
Here is a sample hello.xls
file, with mixed comma and space delimiters. The first program will not work with this file, unless the commas are removed.
3 4 2 3 3 4 3 2 3 3 4 4
2 2 3 4 2 3 3 2 3 2 2 3
1 2 3, 3 2 3 3, 3, 4 2 3 3
3 2 2 3 3 2 1 2 2 2 1 1
4 4 3 4 4 4 4 3 4 4 4 4
4, 3, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4
2 3 2 2 2 1 2 2 2 3 2 2
3 4 4 4 4 4 4 4 4 4 4 4
3 2 3 3 2 3 3 3 2 3 3 3
4 1 4 3 4 4 4 2 4 4 4 4
Here is the output of the second program with this grade file:
Student 0 GPA: 3.17
Student 1 GPA: 2.58
Student 2 GPA: 2.67
Student 3 GPA: 2.00
Student 4 GPA: 3.83
Student 5 GPA: 3.83
Student 6 GPA: 2.08
Student 7 GPA: 3.92
Student 8 GPA: 2.75
Student 9 GPA: 3.50