dawsonrose
dawsonrose

Reputation: 33

How do I correctly pass the contents of a file to qsort using a structure?

I'm trying to make the program read a text file, which contains info like "1001 name surname 10 20 30", the number of the students, name, surname and 3 of his grades. Here's my code:

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

 struct Student {
     int number;
     char name[30];
     char surname[30];
     int midterm1,midterm2,midterm3;
 } Student;

 int comp(const void * aa, const void * bb)
 {
    struct Student a = *(struct Student*)aa;
    struct Student b = *(struct Student*)bb;
    if (a.midterm1==b.midterm1)
            return 0;
    else if (a.midterm1 < b.midterm1)
            return -1;
    else
            return 1;
  } // comp


  int main(void)
  {
      int choice,studentnumber,midterm1,midterm2,midterm3,i,n;
      char surname;
      FILE *cfPtr;

      struct student *name;
      name = malloc( 10 * sizeof(Student));

      if ((cfPtr = fopen("grades.txt", "r")) == NULL)
          return 1;

      const int STUDENTSMAX = 100;
      struct Student students[STUDENTSMAX];
      char buff[1024];
      while(1)
      {
          memset(buff, 0, sizeof(buff));
          fgets(buff, sizeof(buff) -1, cfPtr);
          if (feof(cfPtr)) {
              break;
          }
          sscanf(buff, "%d %s %s %d %d %d", &students[i].number, students[i].name, students[i].surname, &students[i].midterm1, &students[i].midterm2, &students[i].midterm3);
          printf("%4d %15s %15s %10d %10d %10d\n", students[i].number, students[i].name, students[i].surname, students[i].midterm1, students[i].midterm2, students[i].midterm3);
          i++;
      } // while

      while (!feof(cfPtr))
      {
          fscanf(cfPtr, "%d%s%s%d%d%d", &students[i].number, &students[i].name,&students[i].surname, &students[i].midterm1, &students[i].midterm2, &students[i].midterm3);
          printf("%4d%15s%15s%10d%10d%10d\n", students[i].number, students[i].name,students[i].surname, students[i].midterm1, students[i].midterm2, students[i].midterm3);
           i++;
      } // while

      printf("What would you like to do? \n"
      "1- Sort according to midterm 1\n"
      "2- Sort according to midterm 2\n"
      "3- Sort according to midterm 3\n"
      "4- Exit\n");
      scanf("%d",&choice);

      scanf("%d",&choice);
      switch (choice) {
          case 1:
              qsort(students, i, sizeof(struct Student), comp);
              for (n = 0;  n < i; n++) {
                  printf("%4d %15s %15s %10d %10d %10d\n", students[n].number, students[n].name, students[n].surname, students[n].midterm1);
              } // for
              break;    
          case 2:
              qsort(students, i, sizeof(struct Student), comp);
              for (n = 0;  n < i; n++) {
                  printf("%4d %15s %15s %10d %10d %10d\n", students[n].number, students[n].name, students[n].surname, students[n].midterm2);
              } // for
              break;   
          case 3:
              qsort(students, i, sizeof(struct Student), comp);
              for (n = 0;  n < i; n++) {
                  printf("%4d %15s %15s %10d %10d %10d\n", students[n].number, students[n].name, students[n].surname, students[n].midterm3);
              } // for
              break;                          
      } // switch
  } // main?                  
  fclose(cfPtr);


  system("PAUSE");  
  return 0;
  } // the editor failed to find the corresponding '{' !OP should fix this

This current code returns me with this error:

95 C:\Users\UseR\Desktop\main.c [Warning] parameter names (without types) in function declaration 
95 C:\Users\UseR\Desktop\main.c [Warning] data definition has no type or storage class 
98 C:\Users\UseR\Desktop\main.c syntax error before string constant 
98 C:\Users\UseR\Desktop\main.c [Warning] data definition has no type or storage class 
 C:\Users\UseR\Desktop\Makefile.win [Build Error]  [main.o] Error 1 

The program is compiling and working without any problems, yet it still gives the error and doesn't sort at all. What am I doing wrong here?

EDIT: Here is my imput file;

100201 al beaver 40 50 70
100202 andrew matthews 30 90 75
100203 leah doga 60 55 80
100204 rob kurt 45 80 60
100205 aliah devon 65 70 50
100206 sally pir 70 40 85
100207 eric bekta 75 65 55
100208 nile coul 55 75 65
100209 mina umur 72 60 90
100210 john hot 73 63 87

P.S. Yes, this is a homework before anyone asks, I'm running out of time and I've been really trying for days without any solid progression. The thing I'm asked mainly was "

  1. Sort grades of chosen exam in decreasing order and output file will contain only three columns: names,surnames and scores of chosen exam.
  2. Define a structure named “student”, to store name,surname and scores of each student under the same variable name. So all data in the file will be stored in an array of new data type named “student”."

Upvotes: 1

Views: 1041

Answers (2)

user50049
user50049

Reputation:

You have a lot of problems in this code, I'm going to go over what I changed section by section. Since this is homework, I'm not going to post the fully modified program, but just enough information you (or someone in the future finding this) can use to get your code working.

The first thing is, you don't need a type defined structure. Just declare a structure:

Change this:

 typedef struct {
 int number;
 char name[30];
 char surname[30];
 int midterm1,midterm2,midterm3;
 } Student;

To this:

struct Student {
        int number;
        char name[30];
        char surname[30];
        int midterm1,midterm2,midterm3;
};

The second problem is that your compare function is prototyped incorrectly. qsort() expects the callback to take exactly two arguments of the type const void*, so change it to look like this:

int comp(const void * aa, const void * bb)

That will get rid of the warning you see, however it really was just a warning. You're not calling qsort() with the correct arguments. We'll get to that later, I found other problems while running your program.

The third problem is not handling the file failing to open, which results in a segmentation fault (teachers generally give bad grades for seg faults), so instead of this:

 if ((cfPtr = fopen("grades.txt", "r")) == NULL)
    printf("File cannot be opened.\n");
 else {

Just exit, you can't run, no point in indenting anymore with the logic that would never be reached. E.g.:

 if ((cfPtr = fopen("grades.txt", "r")) == NULL) {
      printf("File cannot be opened.\n");
      return 1;
 }

The fourth problem is, you were reading bogus input from the file and not testing for that. I prefer to use a buffer and sscanf() rather than fscanf(), but that's just preference. Anyway, that's how I did it:

    const int STUDENTSMAX = 100;
    struct Student students[STUDENTSMAX];
    char buff[1024];
    while (1)
    {
            memset(buff, 0, sizeof(buff));
            fgets(buff, sizeof(buff) -1, cfPtr);
            if (feof(cfPtr)) {
                    break;
            }
            sscanf(buff, "%d %s %s %d %d %d", &students[i].number, students[i].name, students[i].surname, &students[i].midterm1, &students[i].midterm2, &students[i].midterm3);
             printf("%4d %15s %15s %10d %10d %10d\n", students[i].number, students[i].name, students[i].surname, students[i].midterm1, students[i].midterm2, students[i].midterm3);
            i++;
    }

Note, you have to add #include <string.h> if you go that route and use memset() to reset the buffer on each iteration. Note also, I don't dereference the char[] arrays you have declared in the structure (see the absence of & when explaining name and surname to the pattern given).

Then, this code:

scanf("%d",&choice);

 while (choice != 4) {


  switch (choice) {

       case 1:
            qsort(students,10,sizeof(int),comp);
        printf("%4d%15s%15s%10d%10d%10d\n", students[i].number, students[i].name,students[i].surname, students[i].midterm1);

   }
   }        
   } 

... is going into an infinite loop. Once they select 1 the structure will just be sorted and printed forever so fast because there's no option to handle any other option to exit, and it flies by so fast the user couldn't enter it anyway. I changed it to this, which is quite a bit more sensible:

    scanf("%d",&choice);
    switch (choice) {
            case 1:
            qsort(students, i, sizeof(struct Student), comp);
            for (n = 0;  n < i; n++) {
                     printf("%4d %15s %15s %10d %10d %10d\n", students[n].number, students[n].name, students[n].surname, students[n].midterm1, students[n].midterm2, students[n].midterm3);
            }
            break;
    }

Note the break. You previously did not break out of switch() or while() where you needed to do both. There is no while() around switch() in the code I modified.

Now, the modified call to qsort() should show you the cause of your input not being sorted. You had this:

qsort(students,10,sizeof(int),comp);

The second argument can just be i, since i knows how many elements you have. The third argument needs to be the size of the members you're sorting, not the sizeof an integer (not quite sure how you wandered down that path). This is why the sorted structure was being truncated.

From the man page:

   void qsort(void *base, size_t nmemb, size_t size,
              int(*compar)(const void *, const void *));

DESCRIPTION The qsort() function sorts an array with nmemb elements of size size. The base argument > points to the start of the array.

So as you see, it wants (in this order):

  • What are you sorting?
  • How many members does it have?
  • What is the size of each member?
  • What callback function should I use to make decisions?

Since you're going to and from void * and Student in your callback, you'll need to modify it:

int comp(const void * aa, const void * bb)
{
        struct Student a = *(struct Student*)aa;
        struct Student b = *(struct Student*)bb;
        if (a.midterm1 == b.midterm1)
                return 0;
        else if (a.midterm1 < b.midterm1)
                return -1;
        else
                return 1;
}

See this answer for more details on that.

Final nit, main should be int main(void), since you aren't expecting arguments.

There's some other modifications you'll need to make, or perhaps you could quickly fix what you have based on the feedback I provided. I got rid of all of the unused values, the structure you had allocated (not sure what that was for) and really cleaned it up. I suggest you do the same if you want to get a good grade.

There is also a lot that I didn't fully explain, because this answer is already long enough and I don't want to short circuit more research on your part.

Upvotes: 5

Mahmoud Fayez
Mahmoud Fayez

Reputation: 3459

The compare function should take two pointers to student. it should be like this:

 int comp(const Student * a, const Student * B)
 {
 if (a->midterm1==b->midterm1)
 return 0;
 else
 if (a->midterm1 < B->midterm1)
        return -1;
 else
  return 1;
 }

also the qsort call should be like this

qsort(students, 10, sizeof(Student), comp);

finally why you pass 10 to qsort it should be the actual number of students read from the file.

Upvotes: 0

Related Questions