1
\$\begingroup\$

Or in other words, a student database program.

What does the code do?

  • Checks if there is a text file - if not, creates one with table such as:

student's name student's surname student's id and so on.

  • If the file exists, creates a student from the struct, takes data and prints one student's info to a line, other student's info will be in the next line

  • It can find the student depending on their ID, phone number, mail (but because im doing a char search from lines, you can basically write the name as mail and it'll work out :D)

  • When the data given to be searched is found, it prints out that whole line where it was found.

  • It can order the students in an alphabetical order by checking their name, or ascending order depending on the ID

Why did i code it?

  • It was a project to improve myself that i decided to code on my own. I wanted to practice file processes mostly. This was the best i could do as a first grader at university. I'm a beginner and i'm coding in C to get a grasp on fundementals properly.

  • I tried to keep the code clean and readable as much as possible, being careful about indendation, and reusable code, though at some point it kinda became a mess.

Student.h

#ifndef STUDENT_H_INCLUDED
#define STUDENT_H_INCLUDED


enum user_inputs{NO_INPUT_TAKEN_YET =0, ADD_STUDENT=1, FIND_STUDENT =2, LIST_STUDENT=3, EXIT=4,

                 ACCORDING_TO_ID = 2, ACCORDING_TO_PHONE_NUMBER=1, ACCORDING_TO_MAIL=3, PREVIOUS_MENU =4,

                 ACCORDING_NAME_LIST = 1,
                    };

struct Student{

    char student_name[25];
    char student_surname[25];

    long long  student_id;
    char student_phone_number[30];

    char student_mail[100];

};
#endif

Main.c

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

void main_menu();
void operation_depending_on_user_input(short int user_input);
enum user_inputs;
void add_student();
void find_student();
void list_student();
FILE *filePtr;
void finding_data_in_text_file();
void listing_name();
void listing_id();

int main(){

    main_menu();
}

void main_menu(){
    short int user_input = NO_INPUT_TAKEN_YET;
        while(1){
            printf("1-> Add Students\n"
                   "2-> Find Students\n"
                   "3-> List Students\n"
                   "4-> Exit\n");

            scanf("%hd",&user_input);
            operation_depending_on_user_input(user_input);
    }
}

void operation_depending_on_user_input(short int user_input){
    switch(user_input){
        case ADD_STUDENT:
                add_student();
                break;
        case FIND_STUDENT:
                find_student();
                break;
        case LIST_STUDENT:
                list_student();
                break;
        case EXIT:
            exit(0);
        default:
            printf("\n%s\n","Invalid input, please try again.");
            main_menu();

    }
}

void add_student(){

    int user_input;
    struct Student Student1;


    if(fopen("student_informations.txt","r") == NULL){ // if the file is empty, it'll turn NULL, therefore i check if file has info in it or not.
        filePtr = fopen("student_informations.txt","w");//we create a table at the top of the text file, if the file is empty
        fprintf(filePtr,"#Student's Name#\t"
                    "#Student's Surname#\t"
                    "#Student's ID#\t"
                    "#Student's Phone Number#\t"
                    "#Student's E-Mail#\n\n"); //it just doesnt work without 2 new lines if i want to take long long ints from the file.
                fclose(filePtr);
                add_student();

    }else{ //file isn't empty so else will start to run, and here we'll start to add information.
            filePtr = fopen("student_informations.txt","a");

            printf("Enter Student's Name: ");
                scanf("%s",Student1.student_name);
                fprintf(filePtr,"%s\t",Student1.student_name);

            printf("\nEnter Student's Surname: ");
                scanf("%s",Student1.student_surname);
                fprintf(filePtr,"%s\t",Student1.student_surname);

            printf("\nEnter Student's ID: ");
                scanf("%lld",&Student1.student_id);
                fprintf(filePtr,"%lld\t",Student1.student_id);

            printf("\nEnter Student's Phone Number: ");
                scanf("%s",Student1.student_phone_number);
                fprintf(filePtr,"%s\t",Student1.student_phone_number);

            printf("\nEnter Student's E-Mail: ");
                scanf("%s",Student1.student_mail);
                fprintf(filePtr,"%s\n",Student1.student_mail);
        printf("%s","Do you want to add more students to the list? Press 1 for yes, 2 for no...\n\n");
        scanf("%d",&user_input);
                if(user_input == ADD_STUDENT){
                    fclose(filePtr);
                    add_student();
                }else{
                    fclose(filePtr);
                }
    }
}

void find_student(){

    int user_input;
    printf("%s","\n\n1->Find student according to the phone number\n"
           "2-> Find student according to the ID\n"
           "3->Find student according to the E-Mail\n"
           "4->Go back to previous menu\n");
    scanf("%d",&user_input);

    if(user_input == ACCORDING_TO_PHONE_NUMBER){
        printf("%s","Please give an input of the phone number\n");
            finding_data_in_text_file();
    }else if(user_input == ACCORDING_TO_ID){
        printf("%s","Please give an input of student's ID\n");
            finding_data_in_text_file();
    }else if(user_input == ACCORDING_TO_MAIL){
        printf("%s","Please give an input of student's e-mail\n");
            finding_data_in_text_file();
    }else if(user_input == PREVIOUS_MENU){
        main_menu();
    }else{
        printf("%s","Invalid input, try again");
        find_student();
    }
}

void list_student(){

    int user_input;
    printf("%s","\n\n1->List according to the Name\n"
           "2->List according to the ID\n"
           "3->Go back to previous menu\n");
    scanf("%d",&user_input);
    if(user_input == ACCORDING_NAME_LIST){
            listing_name();
    }else if(user_input == ACCORDING_TO_ID){
            listing_id();
    }else if(user_input == 3){
        main_menu();
    }else{
        printf("%s","Invalid input, try again");
        list_student();
    }


}

void finding_data_in_text_file(){
    char char_input[50];
    char string[500];
    scanf("%s",char_input);
    filePtr = fopen("student_informations.txt","r");
    int line=1;

        while(fgets(string,sizeof(string),filePtr) != NULL){
            if(line == 1){
                fputs(string,stdout);
            }else{
                if(strstr(string,char_input)){
                    fputs(string, stdout);
                }
            }
            line++;
        }
    fclose(filePtr);
}

void listing_name(){
    filePtr = fopen("student_informations.txt","r");
    char unlisted_names[100][100],s[50];
    char string[500],stringgg[500];
    int line=-1;

        while(fgets(string,sizeof(string),filePtr) != NULL){
            if(line==-1){

            }else{
                sscanf(string,"%s",unlisted_names[line]);
            }
            line++;
        }

    for(int i=0;i<line;i++){
      for(int j=i+1;j<line;j++){
         if(strcmp(unlisted_names[i],unlisted_names[j])>0){
            strcpy(s,unlisted_names[i]);
            strcpy(unlisted_names[i],unlisted_names[j]);
            strcpy(unlisted_names[j],s);
         }
      }
   }
    fclose(filePtr);

    int loop=0;
    for(int i=0;i<line;i++){
        filePtr = fopen("student_informations.txt","r"); // opening and closing back so that while loop will work, and cursor will go  back to the line 1
        while(fgets(stringgg,sizeof(stringgg),filePtr) != NULL){
                if(loop ==0){
                    //fputs(stringgg,stdout);
                    loop++; //to print out title at the top like #student name#  #student number# and so on
                }
                if(strstr(stringgg,unlisted_names[i])){
                    fputs(stringgg,stdout);
                    break;
            }
        }
   fclose(filePtr);
   }
}

void listing_id(){
    int line=-1;
    char temp[500],temp1[500],temp2[500],temp3[500];
    long long unlisted_id[50],listed_id[50];
    char string[500],stringgg[500];

    filePtr = fopen("student_informations.txt","r");
    while(fgets(string,sizeof(string),filePtr) != NULL){
        if(line==-1){
            //fputs(string,stdout);
        }else{
            fscanf(filePtr,"%s %s %lld %s %s",temp,temp1,&listed_id[line],temp,temp1);
            unlisted_id[line]=listed_id[line];
        }
        line++;
    }
    fclose(filePtr);
    long long hold;

    for (int i=0;i<line-2;i++){ //bubble sort
        for (int i=0;i<line-2;i++){
            if (listed_id[i]>listed_id[i+1]){
                hold=listed_id[i+1];
                listed_id[i+1]=listed_id[i];
                listed_id[i]=hold;
            }
        }
    }

    int loop=0;
    for(int i=0;i<line-1;i++){
        filePtr = fopen("student_informations.txt","r"); // opening and closing back so that while loop will work, and cursor will go  back to the line 1

        while(fgets(stringgg,sizeof(stringgg),filePtr) != NULL){
                if(loop ==0 || loop ==1){
                    fputs(stringgg,stdout);
                    loop++;
                }else{
                    fscanf(filePtr,"%s %s %lld %s %s",temp,temp1,&unlisted_id[0],temp2,temp3);
                    if(listed_id[i]==unlisted_id[0]){
                        printf("%s %s %lld %s %s\n",temp,temp1,listed_id[i],temp2,temp3);
                        break;
                    }
                }
        }
    fclose(filePtr);
    }
}

Test Input

student_informations.txt

#Student's Name#    #Student's Surname# #Student's ID#  #Student's Phone Number#    #Student's E-Mail#

can nazli   21118080065 056-81-312  [email protected]
ilyas   pkeguc  21118080465 568-965-8   [email protected]
yasin   ina 256983  371-965-46312   [email protected]
```
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Names are long and contain spaces

The below is poor as it 1) does not limit input, allowing a buffer overflow. never user "%s" from scanf() without a width limit like "%24s" 2) does not check the return value of scanf() - how did you know user input was successful? 3) does not handle names like "Betty Jo" 4) Assumes the surname comes 2nd. 5) does not validate names. Names like "!@#" OK?

printf("Enter Student's Name: ");
scanf("%s",Student1.student_name);
...
printf("\nEnter Student's Surname: ");
scanf("%s",Student1.student_surname);

Recommend to not use scanf() at all until you understand its weaknesses. Instead read all user input with fgets(), perhaps via a helper function, and then parse the string for name, ID, phone-number, etc.

Assume user input is faulty and/or hostile. Validate before using further.

Avoid partial records

Rather than get student data in pieces, saving at each step, get all the data first. Then qualify it and then save if successful.

Increase productivity, use an auto formatter

\$\endgroup\$
2
  • \$\begingroup\$ How do we handle names like "betty jo"? \$\endgroup\$ Commented Jan 21, 2022 at 9:11
  • 1
    \$\begingroup\$ @user9560064 Read the name in via fgets(), delete any leading/trailing/repeated whitespace. issues Little Bobby Tables \$\endgroup\$
    – chux
    Commented Jan 21, 2022 at 13:03
1
\$\begingroup\$

My initial impression when I was only visually scanning the code was Well Done! The code is very readable, the variable names and function names are clear and state the purpose of the variable or function clearly.

This code should not work.

Warnings from my compiler:
main.c(147,20): warning C4456: declaration of 'i' hides previous local declaration main.c(146,14): message : see declaration of 'i'

There is a least one warning I got that needs to be addressed, there is a serious problem in the function listing_id() the code below:

    for (int i = 0; i < line - 2; i++) { //bubble sort
        for (int i = 0; i < line - 2; i++) {
            if (listed_id[i] > listed_id[i + 1]) {
                hold = listed_id[i + 1];
                listed_id[i + 1] = listed_id[i];
                listed_id[i] = hold;
            }
        }
    }

The code is redefining the integer variable i in the inner loop, this nested loop needs 2 different loop control variables.

Another logic issue that I find troubling is that the function main_menu() is the main loop of the program, but there is a recursive call to it in a several lower level function, this makes the program very hard to trace the logic and find how you get to where you are:

void list_student() {

    int user_input;
    printf("%s", "\n\n1->List according to the Name\n"
        "2->List according to the ID\n"
        "3->Go back to previous menu\n");
    scanf("%d", &user_input);
    if (user_input == ACCORDING_NAME_LIST) {
        listing_name();
    }
    else if (user_input == ACCORDING_TO_ID) {
        listing_id();
    }
    else if (user_input == 3) {
        main_menu();
    }
    else {
        printf("%s", "Invalid input, try again");
        list_student();
    }
}

Only call the main_menu function once

General Review Comments

Avoid Global Variables

A best practice in most programming languages that require variable delcarations is to declare variables as they are needed. One of your global variables, enum user_inputs; is never used in the program. The other global variable, FILE* filePtr; does not seem to need to be a global variable since the file is opened and closed every time it is used, and can be declared locally.

It is very difficult to read, write, debug and maintain programs that use global variables. Global variables can be modified by any function within the program and therefore require each function to be examined before making changes in the code. In C and C++ global variables impact the namespace and they can cause linking errors if they are defined in multiple files. The answers in this stackoverflow question provide a fuller explanation.

Code Organization

Function prototypes are very useful in large programs that contain multiple source files, and that in case they will be in header files. In a single file program like this it is better to put the main() function at the bottom of the file and all the functions that get used in the proper order above main(). Keep in mind that every line of code written is another line of code where a bug can crawl into the code.

Complexity

There is a programming principle called the Single Responsibility Principle that applies here. The Single Responsibility Principle states:

that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by that module, class or function.

I see 3 separate functions that can be created in the following function.

  1. The first call to read the student file
  2. The bubble sort
  3. The second call to read the student file, which may not be necessary.
    void listing_id() {
        int line = -1;
        char temp[500], temp1[500], temp2[500], temp3[500];
        long long unlisted_id[50], listed_id[50];
        char string[500], stringgg[500];

        filePtr = fopen("student_informations.txt", "r");
        while (fgets(string, sizeof(string), filePtr) != NULL) {
            if (line == -1) {
                //fputs(string,stdout);
            }
            else {
                fscanf(filePtr, "%s %s %lld %s %s", temp, temp1, &listed_id[line], temp, temp1);
                unlisted_id[line] = listed_id[line];
            }
            line++;
        }
        fclose(filePtr);
        long long hold;

        for (int i = 0; i < line - 2; i++) { //bubble sort
            for (int i = 0; i < line - 2; i++) {
                if (listed_id[i] > listed_id[i + 1]) {
                    hold = listed_id[i + 1];
                    listed_id[i + 1] = listed_id[i];
                    listed_id[i] = hold;
                }
            }
        }

        int loop = 0;
        for (int i = 0; i < line - 1; i++) {
            filePtr = fopen("student_informations.txt", "r"); // opening and closing back so that while loop will work, and cursor will go  back to the line 1

            while (fgets(stringgg, sizeof(stringgg), filePtr) != NULL) {
                if (loop == 0 || loop == 1) {
                    fputs(stringgg, stdout);
                    loop++;
                }
                else {
                    fscanf(filePtr, "%s %s %lld %s %s", temp, temp1, &unlisted_id[0], temp2, temp3);
                    if (listed_id[i] == unlisted_id[0]) {
                        printf("%s %s %lld %s %s\n", temp, temp1, listed_id[i], temp2, temp3);
                        break;
                    }
                }
            }
            fclose(filePtr);
        }
    }

DRY Code

There is a programming principle called the Don't Repeat Yourself Principle sometimes referred to as DRY code. If you find yourself repeating the same code multiple times it is better to encapsulate it in a function. If it is possible to loop through the code that can reduce repetition as well. The function above may be violating this principle as well.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.