0

I'm trying to create a C program which collect's an applicant's information. When a user is prompted to enter their written subjects, the program writes rubbish data into the .csv file when they wrote one. And sometimes does the same when the number of subjects written is two.

I've tried to clear the buffer stream, but it's no use. Strangely, using different compliers like DevC++, Embarcadero DevC and VS Code produces different results.

Edit: I've also noticed the chances of the rubbish values being written into the file are lowered when the grades of the subjects is lower than the number of subjects written.

Attached below is the code. And an image of the output.

enter image description here

// C libraries.
#include <stdio.h>   // Contains function prototypes for the standard input/output library functions, and information used by them.
#include <conio.h>   // Contains function prototypes for the console input/output library functions.
#include <stdlib.h>  // Contains function prototypes for conversions of numbers to text and text to numbers, memory allocation, random numbers and other utility functions.
#include <string.h>  // Contains function prototypes for string-processing functions.
#include <time.h>    // Contains function prototypes and types for manipulating the time and date.
#include <stdbool.h> // Contains macros defining bool, true and false, used for boolean variables.

struct Applicant
{
    int applicationID;
    int dateOfApplication;
    char lastName[21];
    char firstName[21];
    char middleName[21];
    char dateOfBirth[21];
    int age;
    char gender;
    char address[100];
    char phoneNumber[21];
    char emailAddress[51];
    char mobileNumber[21];
    int numSubjectsWritten;
    char csecSubjects[20][100];
    char grades[20];
    char programmeSelection[10];
};

struct Applicant getApplicantData()
{
    struct Applicant applicant;
    int i = 0;
    int numSubjects;

    // Asking for applicant input for various fields.
    printf("| Personal |");
    printf("\nEnter Last Name: ");
    scanf(" %20s", &applicant.lastName);
    fflush(stdin);

    printf("\nEnter First Name: ");
    scanf(" %20s", &applicant.firstName);
    fflush(stdin);

    printf("\nEnter Middle Name (If you don't have a middle name, leave this field blank.): ");
    gets(applicant.middleName);
    fflush(stdin);

    /*
    printf("\nEnter Date of Birth: ");
    scanf(" %s", &applicant.dateOfBirth);
    fflush(stdin);
    
    printf("\nEnter Gender. 'M' for male, 'F' for female, (M|F): ");
    scanf(" %c", &applicant.gender);
    fflush(stdin);

    printf("\n\n| Contact Information |");
    printf("\nEnter Address: ");
    gets(applicant.address);
    fflush(stdin);
    
    printf("\nEnter Phone Number: ");
    gets(applicant.phoneNumber);
    fflush(stdin);
    
    printf("\nEnter Email Address: ");
    gets(applicant.emailAddress);
    fflush(stdin);
    
    printf("\nEnter Mobile Number: ");
    gets(applicant.mobileNumber);
    fflush(stdin);
    */

    printf("\n\n| Education |");
    printf("\nEnter Number of Subjects Written: ");
    scanf("%d", &applicant.numSubjectsWritten);
    fflush(stdin);

    while (i < applicant.numSubjectsWritten)
    {

        printf("\nEnter the subject: ");
        gets(applicant.csecSubjects[i]);
        fflush(stdin);

        printf("\nEnter the grade for that subject: ");
        scanf(" %c", &applicant.grades[i]);
        fflush(stdin);

        i++;
    }

    return applicant;
}

int main(void)
{
    FILE *file = fopen("Data.csv", "a+");
    int i, j;

    if (!file)
    {
        printf("\nError! Can not open data file.\nPlease contact the Program Addmission Manager as soon as possible with the error message.");
        exit(1);
    }

    else
    {
        struct Applicant applicant = getApplicantData();

        //fprintf(file, "%s:%s:%s:%s:%c:%s:%s:%s:%s", applicant.lastName, applicant.firstName, applicant.middleName, applicant.dateOfBirth, applicant.gender, applicant.address, applicant.phoneNumber, applicant.emailAddress, applicant.mobileNumber);

        fprintf(file, "%s:%s:%s:", applicant.lastName, applicant.firstName, applicant.middleName);

        for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)
        {
            fprintf(file, " %s", applicant.csecSubjects[i]);
            fflush(stdout);
            fflush(stdin);
            fflush(file);

            fprintf(file, " ( %c):", applicant.grades[i]);
            fflush(stdout);
            fflush(stdin);
            fflush(file);
        }
    }

    return 0;
}
Bigaku Acedia
  • 11
  • 1
  • 2
  • 1
    You can't mix `scanf` and `gets`. (And you really shouldn't use `gets` at all.) Try replacing your `gets` calls with `scanf("%s")`. (But if you want to be able to read strings with spaces, it'll get more complicated.) – Steve Summit Jul 16 '21 at 20:18
  • 1
    I guess you were using `gets` because you wanted to let the user leave some fields blank, by just pressing Enter. But you're going to have to decide between an easy-to-write program, using `scanf`, that has limitations in what it can read, or an almost-impossible-to-write program, using `scanf`, that reads input robustly, or a medium-difficulty program to write, using something other than `scanf`, that reads input robustly. See [here](https://stackoverflow.com/questions/58403537) for information about alternatives to `scanf`. – Steve Summit Jul 16 '21 at 20:23
  • https://stackoverflow.com/questions/2979209/using-fflushstdin – William Pursell Jul 16 '21 at 20:26
  • 1
    `scanf` is good -- barely -- for reading a few simple numbers in the first two or three C programs you write. But it turns out `scanf` is reasonably horrible for anything complicated -- and your program here is on the cusp of being complicated. In other words, your program here is on the edge of what `scanf` can reasonably do. Unfortunately your instructor -- like 99% of the C instructors in the world -- has given you the impression that `scanf` is a fine way, and the default way, to do input in C. But it is not. – Steve Summit Jul 16 '21 at 20:26
  • @SteveSummit Thank you, brother. I've changed the code to use only 'scanf', but it still writes rubbish values. Also, I've made an edit to update the scenario when it happens. – Bigaku Acedia Jul 16 '21 at 20:27
  • `fflush(stdin);` is has undefined behaviour. `fflush()` is defined for output stream only. In Microsoft's implementation it clears the input buffer, but it is not portable. – Clifford Jul 16 '21 at 20:28
  • The program manager will probably not be able to get much information from the error message `"\nError! Can not open data file.\nPlease contact the Program Addmission Manager as soon as possible with the error message."` Try `perror("Data.csv");` – William Pursell Jul 16 '21 at 20:28
  • Avoid posting _pictures of text_, post the text. To be clear also post the expected output. It is not obvious what is "rubbish" and what is intentional. What was the input? – Clifford Jul 16 '21 at 20:30
  • If you use only `%d`, `%f`, and `%s`, and if you don't intersperse any calls to `gets`, `getchar`, or `gets`, and if you either don't use `%c` or remember always to precede it by a space (as in fact you do here), and if you never make any mistakes while typing in data, you can probably get code like this, using `scanf`, to work. (Just don't make any mistakes while typing in data.) (Also if you follow these rules, you won't need `fflush(stdin)`, which is problematic in its own right.) – Steve Summit Jul 16 '21 at 20:31
  • @Clifford Actually, in this one case, I was fine with the picture of the output, because it let me see the OP's actual nonprintable garbage. – Steve Summit Jul 16 '21 at 20:41
  • @BigakuAcedia Your other problem is that this loop is no good: `for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)`. You might want to just store the number of courses in `struct Applicant`. – Steve Summit Jul 16 '21 at 20:43
  • You probably know this, and your current output is probably just temporary, for debugging, but CSV usually means *comma* separated, not colons. – Steve Summit Jul 16 '21 at 20:53

2 Answers2

2

First problems I see:

  1. Remove the & from all instances where you scanf a string

  2. Don't use gets, or mix scanf and fgets

  3. Don't fflush(stdin)

yano
  • 4,827
  • 2
  • 23
  • 35
0

Instead of scanf, consider using a custom-made input method with condition checking and anything you need. I will give an example.

#define BUFFER_SIZE 512

void input(char* buffer){ 
  memset(buffer, 0, BUFFER_SIZE); // Initializing the buffer.
  fgets(buffer, BUFFER_SIZE, stdin);

  strtok(buffer,"\n");
}  

How to take input using that?

void main(){
  int username[BUFFER_SIZE];
  input(username);
}  

A way to write a structure to a file is shown below.

 void Structure_Print(Applicant* applicant, FILE* stream, int no_of_applicant){
   if(no_of_applicant==0){
     fprintf(stdout, "No applicant yet.\n");
     return;
   }
       fprintf(stream, "%s:%s:%s:", applicant.lastName, applicant.firstName, applicant.middleName);
for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)
    {
        fprintf(stream, " %s:", applicant.csecSubjects[i]);
        fprintf(stream, " %c:", applicant.grades[i]);
    }
   return;
 }  

Also, I noticed how you tried to make it readable while saving it in subject(grade) format. I recommend you to not do that. Your .csv file is just for database. Nobody is going to read it. So just store the data by comma or any character separator. It will make it easier to extract data later.

Daoist Paul
  • 133
  • 8