cdatabasebin

How to filter database more than one time with C


I need to filter students by the courses they take. I need to filter students, who has "Algebra", but I can't figure it out, because in the first if statement it prints all of the students. But I need to filter with the second if statement by the courses. What should I do with the first if statement?

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

typedef struct Student {
    char name[30];
    char surname[30];
    int course;                 // year of study
    double average;             // average grade
    
    int load;                   // number of courses
    char courses[10][30];       // course names
    int grades[10];             // course grades    
    
    char languages[100];        // spoken languages
    
} Student;


int main(int argc, char *argv[]) {
    FILE *db = NULL;
    // open database file for reading, provide a parameter or use default "db.bin"
    if (argc > 1)
        db = fopen(argv[1], "rb");
    else
        db = fopen("db.bin", "rb");
        
    if (db){                            
        Student students[1000];         // all the data goes here
        int size = 0;                   // how many students in database
        
        // reading data from file
        fread(&size, sizeof(int), 1, db);
        
        for (int i = 0; i < size; i++){         
            fread(&students[i], sizeof(Student), 1, db);            
        }   
        printf("%d records loaded succesfully\n", size);
        
        
        // MODIFY CODE BELOW
        
        int counterDemo = 0; // for counting students

        for (int i = 0; i < size; ++i){ // process all the student records in database
            Student s = students[i]; // store data for each student in s

            if(1){ // *** first filter, conditions on the student
                printf("%s%s%d%f%d", s.name, s.surname, s.course, s.average, s.load); // print student data
                int anotherDemo = 0; // for counting courses/grades
                for (int i = 0; i < s.load; ++i){ // process each course taken by the student
                    if(s.courses[][]){ // *** second filter, conditions on the course/grade
                        ++anotherDemo; // counting courses
                        printf("%s%d", s.courses[i], s.grades[i]);
                    }
                }
                printf("%s\n", s.languages);
                        
                if (anotherDemo == s.load) // *** third filter, various other conditions            
                    ++counterDemo; // counting studfents
            }
        }
        printf("Filter applied, %d students found\n", counterDemo); // how many passed the filters
        fclose(db); 
    } else {
        printf("File db.bin not found, check current folder\n");
    }

    return 0;
}


Solution

  • This is a partial re-write (not compiled and not tested) of your code, based on what it appears you want to achieve. This may suggest an approach that you might follow to get where you want to be. (There may be typos in here, but the general scheme should help you move forward.)

    int filterLoad( Student *p, int n ) {
        return p->load >= n;
    }
    
    int filterCourse( Student *p, char *pCourse, int minGrade ) {
        for( int i = 0; i < sizeof p->courses/sizeof p->courses[0]; i++ )
            if( strcmp( p->courses[ i ], pCourse ) == 0
            &&  p->grades[ i ] >= minGrade )
                return 1; // qualifies
        return 0; // does not qualify
    }
    
    void showStudent( int cnt, Student *p ) {
        printf( "%d: %s, %s\n", cnt, p->surname, p->name );
        /* more printing, including finding Algebra mark again, etc. */
    }
    
    int main( int argc, char *argv[] ) {
        char dbName = "db.bin"; //default
    
        if( argc > 1 )
            dbName = argv[1];
    
        FILE *db = fopen( dbName, "rb" );
        if( db == NULL ) {
            fprintf( stderr, "Cannot open '%s'. Check current folder\n", dbName );
            return 1;
        }
    
        Student students[1000];
        int size = 0;
    
        fread( &size, sizeof size, 1, db ); // # of records to load
    
        for( int i = 0; i < size; i++ )
            fread( &students[i], sizeof students[0], 1, db );
    
        fclose( db ); // done with file
    
        printf( "%d records loaded succesfully\n", i );
    
        int counter = 0;
        for( int i = 0; i < size; i++ ) {
            Student *pStu = students[ i ];
    
            if( filterCourse( pStu, "Algebra", 70 ) // attained min 70 in Algebra
            &&  filterLoad( pStu, 3 ) ) // and min of 3 subjects
                showStudent( ++counter, pStu );
        }
        printf( "%d matching students found\n", counter );
    
        return 0;
    }
    

    There's no reason to read up to 1000 records at once. You could use just a single buffer to load one-record-at-at-time. It's your choice.

    int main( int argc, char *argv[] ) {
        char dbName = "db.bin"; //default
        if( argc > 1 )
            dbName = argv[1];
    
        FILE *db = fopen( dbName, "rb" );
        if( db == NULL ) {
            fprintf( stderr, "Cannot open '%s'. Check current folder\n", dbName );
            return 1;
        }
    
        fseek( db, sizeof(int), SEEK_SET ); // step over useless number
    
        int counter = 0;
        Student one;
        for( int i = 0; fread( &one, sizeof one, 1, db ) != NULL; i++ )
            if( filterCourse( &one, "Algebra", 70 )
            &&  filterLoad( &one, 3 ) )
                showStudent( ++counter, &one );
    
        fclose( db );
        printf( "%d student records examined\n", i );
        printf( "%d matching students found\n", counter );
    
        return 0;
    }