I'm having trouble sorting this file, giving each line an index. The whole point is to prompt the user to type in the index so the program can return the program line that corresponds to the index number. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void printUnsortedStringFromFile(int amount, char A[]);
void printSortedStringFromFile(int amount, char A[]);
//bool binSearchNUM(int amount, int A[amount], int target, int *current);
int main()
{
FILE* spData = fopen("grades.csv", "r");
int ch, number_of_lines = 0;
do
{
ch = fgetc(spData);
if (ch == '\n')
number_of_lines++;
} while (ch != EOF);
if (ch != '\n' && number_of_lines != 0)
number_of_lines++;
fclose(spData);
printf("There are %d lines in file grades.csv . \n", number_of_lines);
int amount = number_of_lines;
char A[amount];
printUnsortedStringFromFile(amount, A);
printSortedStringFromFile(amount, A);
return 0;
}
void printUnsortedStringFromFile(int amount, char A[])
{
FILE *spData;
spData = fopen("grades.csv", "r");
if(spData == NULL)
{
fprintf(stderr, "Error opening the file grades.csv.\n");
exit(1);
}
int ex1;
int ex2;
int ex3;
int StudentNUM;
char StudentAVG;
printf("+-------+------+------+------+-----+\n");
printf("|Student|Exam 1|Exam 2|Exam 3|Grade|\n");
printf("+-------+------+------+------+-----+\n");
int z = 0;
while((fgets(A, amount, spData)) != NULL)
{
sscanf(A, "%d, %d, %d, %d, %c", &StudentNUM, &ex1, &ex2, &ex3, &StudentAVG);
printf("| %d| %d| %d| %d| %c| \n", StudentNUM, ex1, ex2, ex3, StudentAVG);
z++;
//prints unsorted correctly
}
printf("+-------+------+------+------+-----+\n");
if (fclose(spData) == EOF)
{
fprintf(stderr, "Error closing the file grades.csv. \n");
exit(2);
}
}
void printSortedStringFromFile(int amount, char A[])
{
FILE *spData;
spData = fopen("grades.csv", "r");
if(spData == NULL)
{
fprintf(stderr, "Error opening the file grades.csv.\n");
exit(1);
}
//help needed implementing insertion sort to sort each string as an index here
{
int walk;
int temp;
for (int cur = 1; cur < amount; cur++)
{
bool located = false;
temp = A[cur], walk = cur-1;
while (walk >= 0 && !located)
{
if (temp < A[walk])
{
A[walk+1] = A[walk];
walk--;
}
else
{
located = true;
}
}
A[walk+1] = temp;
}
}
int StudentNUM;
char StudentAVG;
printf("+-----+-------+-----+\n");
printf("|Index|Student|Grade|\n");
printf("+-----+-------+-----+\n");
int z = 0;
while((fgets(A, amount, spData)) != NULL)
{
sscanf(A, "%d, %c", &StudentNUM, &StudentAVG);
printf("| %d| %c| \n", StudentNUM, StudentAVG);
z++;
//student ID prints, grade average doesn/t, unsure how to sort these strings into a numbered(index) list
}
if (fclose(spData) == EOF)
{
fprintf(stderr, "Error closing the file grades.csv. \n");
exit(2);
}
}
/* (correct) example output:
There are 5 lines in file grades.csv.
Original:
+-------+------+------+------+-----+
|Student|Exam 1|Exam 2|Exam 3|Grade|
+-------+------+------+------+-----+
| 535743| 67| 96| 93| B|
| 112213| 87| 65| 72| C|
| 612778| 59| 58| 97| C|
| 151774| 52| 100| 86| C|
| 406704| 54| 72| 80| D|
+-------+------+------+------+-----+
Sorted:
+-----+-------+-----+
|Index|Student|Grade|
+-----+-------+-----+
| 1| 112213| C|
| 2| 151774| C|
| 3| 406704| D|
| 4| 535743| B|
| 5| 612778| C|
+-----+-------+-----+
*/
Answer Part One.
The main problem in your source code is the string char A[amount];
.
In the main()
function the variable A
is allocated to the number of lines ?!!
In your example,
number_of_lines = 5
meansA[amount] = A[5]
is able to store only a 4-characters string + null terminator.
printf("There are %d lines in file grades.csv . \n", number_of_lines);
int amount = number_of_lines;
char A[amount];
printUnsortedStringFromFile(amount, A);
printSortedStringFromFile(amount, A);
Then on both printUnsortedStringFromFile()
and printSortedStringFromFile()
functions the same variable A
is used as a buffer to load and read one line.
In your example, the first line of 'grades.csv' is longer 4 characters and is truncated before calling
sscanf()
.
while((fgets(A, amount, spData)) != NULL)
{
sscanf(A, "%d, %d, %d, %d, %c", &StudentNUM, &ex1, &ex2, &ex3, &StudentAVG);
A solution could be to use a local
char sTmp[80]
for thefgets()
andsscanf()
and use theA[amount]
only for the indexation.
Answer Part Two.
The second problem in your source code is that the suggested indexation in order to ascending student-id sort records by insertion sort, needs to store not only the index but also the content of each record. I suggest to use define a structure as follow:
struct gradesRecord {
int iIndex; // index on the file
int iStudentNUM; // 'Student' field
int iExamVAL[3]; // 'Exam 1'..'Exam 3' fields
char cStudentAVG; // 'Grade' field
};
Then convert your A[]
array from char
to struct gradesRecord
(in main()
):
int amount = number_of_lines;
struct gradesRecord A[amount];
printUnsortedStringFromFile(amount, A);
printSortedStringFromFile(amount, A);
In the printUnsortedStringFromFile()
function, the array A[]
is used directly in the reading loop:
To prevent a bad formatted text-file, it is recommended to check the returned value of
sscanf()
in order to detect missing parameters (see thenArg
variable and how to check bellow).
char sLine[81]; // local string to read one row
int z = 0; // storage index
int nArg;
while((fgets(sLine, 80, spData)) != NULL)
{
nArg = sscanf(sLine, "%d, %d, %d, %d, %c",
&(A[z].iStudentNUM), &(A[z].iExamVAL[0]),
&(A[z].iExamVAL[1]), &(A[z].iExamVAL[2]),
&(A[z].cStudentAVG));
if (nArg != 5) {
// the input line is not correct !!!
// manage that error.
}
printf("|%7d| %5d| %5d| %5d| %c| \n", A[z].iStudentNUM,
A[z].iExamVAL[0], A[z].iExamVAL[1], A[z].iExamVAL[2],
A[z].cStudentAVG);
z++; // next row
Then in the printSortedStringFromFile()
function, the array A[]
is used to store, sort in the reading loop, then displayed in a second loop:
First loop, reading and selection sort of all rows:
char sLine[81];
int iLine = 0, iRow;
struct gradesRecord grRow,grTmp;
while((fgets(sLine, 80, spData)) != NULL)
{
// extract one Row and store it into grRow
sscanf(sLine, "%d, %d, %d, %d, %c",
&(grRow.iStudentNUM), &(grRow.iExamVAL[0]),
&(grRow.iExamVAL[1]), &(grRow.iExamVAL[2]),
&(grRow.cStudentAVG));
// keep the line index of that row
grRow.iIndex = iLine;
// search loop = insertion sort algorithm
for (iRow=0;iRow<iLine;iRow++) {
// detect if new student is before the store one
if (grRow.iStudentNUM < A[iRow].iStudentNUM) {
// exchange both stuident records through grTmp
memcpy(&grTmp,&(A[iRow]),sizeof(struct gradesRecord));
memcpy(&(A[iRow]),&grRow,sizeof(struct gradesRecord));
memcpy(&grRow,&grTmp,sizeof(struct gradesRecord));
}
}
// store the biggest student at the end
memcpy(&(A[iLine]),&grRow,sizeof(struct gradesRecord));
iLine++;
}
Second loop, display the sorted table:
while (z < amount)
{
StudentNUM = A[z].iStudentNUM;
StudentAVG = A[z].cStudentAVG;
index = A[z].iIndex;
printf("| %4d|%7d| %c| \n", index, StudentNUM, StudentAVG);
z++;
}