0

I am trying to create a function which with read from a file some grades with the student names and print the grades in decreasing order. This is the function:

char studenteUN[50];
int punti;
int maxP = MAX_LNG;
int max = -1000;
int min = MAX_LNG;

FILE *ftemp = f;
while(fscanf(ftemp, "%s", studenteUN) != EOF){
  fscanf(ftemp, "%d", &punti);
  if(punti < min)
    min = punti;
}

while (maxP != min) {
  max = -1000;
  ftemp = muovi(f);
  while(fscanf(ftemp, "%s", studenteUN) != EOF){
    fscanf(ftemp, "%d", &punti);
    if(punti > max && punti < maxP)
      max = punti;
  }
  maxP = max;
  ftemp = muovi(f);
  while(fscanf(ftemp, "%s", studenteUN) != EOF){
    fscanf(ftemp, "%d", &punti);
    if(punti == max)
      printf("%s %d\n", studenteUN, punti);
  }
}

Te output is the following:

asdas 8


asdas 7


asdsa 6


andrea 5


asd 4


asd 1
asdsad 1


asdas 0
asd 0

I can not figure it out why this happens. The file I read from is the following:

2
1- askd a
   1- asd
   2- asd as
   3- asd as d
   4- asdas
   5- Non lo so.
1
2- asdsad asd as d
   1- asd a
   2- asd
   3- asd
   4- as df
   5- Non lo so.
2
andrea 5
asdsa 6
asdas 7
asd 1
asdas 0
asd 0
asdsad 1
asdas 8
asd 4

The muovi function:

FILE *muovi(FILE *f){
 fseek(f, 0, SEEK_SET);
 char resultato[MAX_LNG];
 int nD;
 fscanf(f, "%d", &nD);
 printf("\n");
 for(int i = 0; i <= nD*7;i++)
  fgets(resultato, MAX_LNG, f);
 return f;
}
  • 3
    What is `muovi(f)`? Is it the same as `fseek(f, 0, SEEK_SET)`? – Barmar Jul 10 '20 at 16:51
  • In the file this lines are in the second middle, so I use this method to move the pointer to the file to the neccesary place. –  Jul 10 '20 at 16:52
  • 3
    Please [edit] your question to include a [mcve], including an example of the input file you read from. – Some programmer dude Jul 10 '20 at 16:52
  • @Someprogrammerdude I added the file i read from –  Jul 10 '20 at 16:58
  • 1
    I put your code in a `main()` function and I couldn't reproduce the problem. – Barmar Jul 10 '20 at 17:00
  • https://ericlippert.com/2014/03/05/how-to-debug-small-programs/ – Barmar Jul 10 '20 at 17:01
  • Even if the input file is double-spaced, `fscanf` using `%d` and `%s` should filter that out. – Weather Vane Jul 10 '20 at 17:01
  • Are you writing the file in text mode or binary mode? If text mode, the issue may just be how you are viewing the contents of the file. – David Schwartz Jul 10 '20 at 17:02
  • 4
    It's because of `printf("\n");` in `muovi()` – Barmar Jul 10 '20 at 17:02
  • 1
    BTW, this is a horrible way to do this, rereading the file for every place. Read everything into an array of structures, sort the array, and then print the array. – Barmar Jul 10 '20 at 17:03
  • @Barmar first time I am trying to do this so still learning. Any advice would be appreciated –  Jul 10 '20 at 17:05
  • https://stackoverflow.com/questions/8721189/how-to-sort-an-array-of-structs-in-c – Barmar Jul 10 '20 at 17:07
  • @Barmar Would it be a good idea if I used a structure list with pointers? –  Jul 10 '20 at 17:10
  • @EntiolLiko you can also use a big enough array of structs to get your logic to work, and not deal with dynamic allocation – Johnny Andrew Jul 10 '20 at 17:14
  • There are several problems here. The main problem is that you trying to all things in one step - that's a bad design. Break the task into minor tasks that you can do one by one. That is: 1) Read the file into an array 2) Sort the array 3) Print the array – Support Ukraine Jul 10 '20 at 17:33
  • @EntiolLiko That's probably how a pro would do it, so it just sorts pointers instead of copying whole structures. But it's not really that important at tihs stage of learning. – Barmar Jul 10 '20 at 18:07

2 Answers2

0

I've written a demo sample: it reads input of the form 'name grade':

student1 1
student2 7
student3 -4
student4 11
student5 1111
student6 -11111

And outputs:

student5 1111
student4 11
student2 7
student1 1
student3 -4
student6 -11111

Here is the code behind, perhaps you can make an idea

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

struct studs {
    int grade;
    char name[50];
};

int md_comparator(const void *v1, const void *v2)
{
    const struct studs *p1 = (struct studs *)v1;
    const struct studs *p2 = (struct studs *)v2;
    
    if (p1->grade < p2->grade)
        return 1;
    else if (p1->grade > p2->grade)
        return -1;
    else
        return 0;
}


int main()
{
    struct studs students[1000];
    struct studs temp;
    
    //read all students into students[] array
    int idx = 0;
    for (; fscanf(stdin, "%s %d", temp.name, &temp.grade) == 2; students[idx++] = temp)
        ;
    
    //sort descending, with the help of qsort and md_comparator function above
    qsort(students, idx, sizeof(students[0]), md_comparator);
    
    //print
    for (int i = 0; i < idx; i++)
        printf("%s %d\n", students[i].name, students[i].grade);
    
    return 0;
}
  • If you read each line with `fgets()` into a buffer and then use `sscanf` to parse the buffer checking that the return is 2 -- you can read the entire file only picking out the student data. – David C. Rankin Jul 10 '20 at 18:25
0

When reading lines of data from a file, you want to use a line-oriented input function such as fgets() or POSIX getline(). This ensures a complete line of input is consumed during each read. Otherwise when looking for a pattern, like a string followed by an integer, partial reads will likely lead to false-matches depending on what parts of the line are left in the input-buffer unread.

After reading each line into a buffer, you can simply parse the needed information from each line using sscanf() instead of your attempt to both read and parse with fscanf().

Putting that together, it is no problem at all to read and isolate only the student name and number from the file, e.g.

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

#define NAMC 50         /* if you need a constant, #define one (or more) */
#define MAXC 1024

typedef struct {        /* struct for student name and number */
    char name[NAMC];
    int no;
} student;

int cmpstd (const void *a, const void *b)   /* qsort descending by student.no */
{
    const student *pa = a, *pb = b;
    
    return (pa->no < pb->no) - (pa->no > pb->no);
}

int main (int argc, char **argv) {
    
    student std[NAMC];      /* array of student */
    char buf[MAXC];         /* buffer to hold line */
    int n = 0;              /* student counter */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    
    while (n < NAMC && fgets (buf, MAXC, fp)) { /* limit to array sz, read line */
        student tmp;        /* temporary struct for student */
        /* parse name and number from line, validating return */
        if (sscanf (buf, "%s %d", tmp.name, &tmp.no) == 2)
            std[n++] = tmp; /* add to array */
    }
    
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    qsort (std, n, sizeof *std, cmpstd);    /* sort by student.no descending */
    
    for (int i = 0; i < n; i++)             /* output results */
        printf ("%-10s %d\n", std[i].name, std[i].no);
}

Example Use/Output

With your input file in dat/prefixedstd.txt you would receive the following:

$ ./bin/read_prefixstd dat/prefixedstd.txt
asdas      8
asdas      7
asdsa      6
andrea     5
asd        4
asd        1
asdsad     1
asdas      0
asd        0

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thanks for the answer. I have a question. I am trying to write a function which will add a student to the file (so username, password,status(int 1 or 0)). Which would be a better solution: 1- Get all the data from the file and put it into a list, modify the list and in the end write a funtion to write all the changes to the file or 2-Write to the file directy using only 1 function? Thanks –  Jul 11 '20 at 17:29