fprintf function in code writes garbage data into .csv file - c

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.
// 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;
}

First problems I see:
Remove the & from all instances where you scanf a string
Don't use gets, or mix scanf and fgets
Don't fflush(stdin)

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.

Related

printf will not print output of %d correctly for an int

Can't figure out why my printf output won't print the int data.age or addr.zip correctly. They're defined as int, so %d should work...but it doesn't. I get gibberish numbers for the output. All other fields work perfectly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct BillingAddress {
char *street[50];
char *city[25];
char *state[3];
int *zip[6];
}address;
address addr;
typedef struct ContactInfo {
char *name[30];
int *age[3];
char *phone[15];
struct BillingAddress PersonalData;
}personaldata;
personaldata data;
int main()
{
printf("Enter your full name: ");
scanf(" %49[^\n]", &data.name);
printf("Enter your age: ");
scanf("%d", &data.age);
printf("Enter your Phone Number (xxx) xxx-xxxx: ");
scanf(" %14[^\n]", &data.phone);
printf("Enter your street address: ");
scanf(" %49[^\n]", &addr.street);
printf("Enter your city: ");
scanf(" %24[^\n]", &addr.city);
printf("Enter your state abbreviation: ");
scanf("%s", &addr.state);
printf("Enter your zip code: ");
scanf("%d", &addr.zip);
printf("Personal Data: \n %s\n %d\n %s\n %s\n %s\n %s\n %d\n", data.name, data.age, data.phone, addr.street, addr.city, addr.state, addr.zip);
}
Let's go step by step. In your BillingAddress struct definition, you intend to store the street, city, and state. It seems logical that you could represent all of those parameters as strings (which are character arrays in C). I believe that is what you tried to do, but the declaration:
char *street[50];
represents give you an array of 50 char * (i.e., char pointers) NOT 50 chars, which is what you actually want. So, the struct definition should instead be:
typedef struct BillingAddress {
char street[50];
char city[25];
char state[3];
int zip;
} address;
You also intend to store the zip. Note how I changed the type of zip to an int array rather than an int array. You could do an int array, but you would have to do more work to properly accept the user's input (simply using scan("%d", &addr.zip) will not work).
Now, let's take a looked at a different way to define your definition for ContactInfo struct:
typedef struct ContactInfo {
char name[30];
int age;
char phone[15];
address addr;
} personaldata;
You can see that I've removed the *'s and changed age to be a single int for the similar reasoning as what I mentioned above. I've also replaced struct BillingAddress with the simpler address. Why? Because you created a typedef linking the keyword address to struct BillingAddress. So, by using address alone, the code looks a bit cleaner and you are putting that typedef to good use :D (Note that there's nothing incorrect about using struct BillingAddress, I just think it looks cleaner to utilize the typedef).
Also, you'll see that I renamed the variable of type address (i.e., of type struct BillingAddress -- remember the typedef means these two are the same) to addr. This makes more sense than naming it PersonalData as you did, because well, it's an address, not arbitrary personal data.
Now moving onto your main() method. You should be aware of some of the pitfalls of using scanf(). Since it reads from the default input stream stdin (i.e., most likely your terminal), any characters not gulped up by scanf() will be left in the input stream. So, any subsequent calls to scanf() will also read in those unwanted characters. See this post for more info. You might not have run into this if you supplied the less than the max number of desired characters, but it is important to take care of this error case. So, after each scanf(), you should clear the input stream, which can be done using a method like:
void clear_input_stream()
{
int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF);
}
You can call this method after each one of your scanf() calls.
You want to make sure that if you are writing into a char array you should not add the & in front of the variable you are writing to -- check this answer for an explanation (your compiler should warn you about that).
The last thing I want to mention is that you could also add a length to the %d specifier to limit reading an integer of a certain length. Your main() could look something like this:
int main()
{
printf("Enter your full name: ");
scanf(" %49[^\n]", data.name);
clear_input_stream();
printf("Enter your age: ");
scanf(" %3d", &data.age);
clear_input_stream();
printf("Enter your Phone Number (xxx) xxx-xxxx: ");
scanf(" %14[^\n]", data.phone);
clear_input_stream();
printf("Enter your street address: ");
scanf(" %49[^\n]", addr.street);
clear_input_stream();
printf("Enter your city: ");
scanf(" %24[^\n]", addr.city);
clear_input_stream();
printf("Enter your state abbreviation: ");
scanf(" %2[^\n]", addr.state);
clear_input_stream();
printf("Enter your zip code: ");
scanf(" %5d", &addr.zip);
clear_input_stream();
printf("Personal Data: \n %s\n %d\n %s\n %s\n %s\n %s\n %d\n", data.name, data.age, data.phone, addr.street, addr.city, addr.state, addr.zip);
return 0;
}

using fgets with structure

I am trying to use fgets with structure, since I have to insert in character array. But when I use fgets it's not working properly. I can not enter value for the char array. Please help. Below is a sample program::
#include <stdio.h>
#include<string.h>
struct Student
{
int roll;
char name[50];
int age;
char branch[50];
char gender[1]; //F for female and M for male
};
int main()
{
struct Student s1;
printf("enter roll number of the student: ");
scanf("%d", &s1.roll);
printf("Enter student name: ");
fgets(s1.name, 50, stdin); // NOT WORKING ...
printf("Enter age number: ");
scanf("%d", &s1.age);
printf("Enter branch number: ");
scanf("%d", &s1.branch);
printf("Enter Gender: ");
scanf("%d", &s1.gender);
return 0;
}
First of all you need different format specifiers for different datatypes. So you need to use %c for a character and %[^\n] for a string containing spaces.
You also need to remove leading whitespaces before scanning a string, because a newline \n is left in the input buffer which would otherwise be read by %c and %[], as Weather Vane pointed out in a comment.
#include <stdio.h>
#include <string.h>
struct student
{
int roll;
char name[50];
int age;
char branch[50];
char gender; // can be a single character
};
int main(void)
{
struct student s1;
printf("Enter roll number: ");
scanf("%d", &s1.roll);
printf("Enter name: ");
scanf(" %49[^\n]", s1.name); // use %[^\n] to scan a string containing spaces
printf("Enter age: ");
scanf("%d", &s1.age);
printf("Enter branch name: ");
scanf(" %49[^\n]", s1.branch);
printf("Enter gender: ");
scanf(" %c", &s1.gender); // %c is the format specifier for a char
return 0;
}
fgets is not being bypassed, it's actually working as it should, what happens is that it reads the newline character that remains in the input buffer from the previous scanf, if you access s1.name you will see that it has a string ("\n\0") in it.
For name I have to insert space character too, so I used fgets
You can use scanf with [^\n] specifier which can read spaces. Mixing scanf with fgets is trouble, it can be done, but you should avoid it.
You should either use scanf only, or fgets only, in the latter case, if you need to convert strings to ints use sscanf or better yet strtol.
Your code has other issues, detailed in the comments with corrections:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
int roll;
char name[50];
int age;
char branch[50];
char gender; //F for female and M for male
};
For solution with scanf only it should, more or less, look like this:
void clear_buffer(){ // helper function to clear buffer
int c;
while((c = getchar()) != '\n' && c != EOF){}
if(c == EOF){
fprintf(stderr, "Fatal error!");
exit(EXIT_FAILURE);
}
}
int main()
{
struct Student s1;
printf("enter roll number of the student: ");
while (scanf("%d", &s1.roll) != 1){
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
} // if bad input ask again
printf("Enter student name: "); // the space before % clears blanks
while (scanf(" %49[^\n]", s1.name) != 1){ // will read the line until
fprintf(stderr, "Bad input, try again: "); // enter is pressed, provided
clear_buffer(); // that it's not larger than 49
}
printf("Enter age number: ");
while(scanf("%d", &s1.age) != 1){
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
}
printf("Enter branch number: ");
while (scanf(" %49[^\n]", s1.branch) != 1){ // branch is a string, %d
clear_buffer(); // specifier is for ints.
fprintf(stderr, "Bad input, try again: "); // note that I'm using width
} // limit (49) to avoid buffer overflow
printf("Enter Gender: ");
while(scanf(" %c", &s1.gender) != 1){ // only 1 character needed, use %c
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
}
}
For a solution with fgets only which, I would argue is better, you can do something like this:
int main(){
struct Student s1;
char temp[50];
printf("enter roll number of the student: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%d", &s1.roll) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter student name: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%49[^\n]", s1.name) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter age number: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%d", &s1.age) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter branch number: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%49[^\n]", s1.branch) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter Gender: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, " %c", &s1.gender) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
}
*scanf to parse ints still has a potencial flaw in case of overflow, there is no way of guarding against that, unless you use a more robust method like the aforementioned strtol.

How to edit data of a file File Handling C

I'm wondering if I could use fseek() on editing the value of a variable on a file. All the tutorials I saw online were about editing a text, but how can I use fseek() to edit a value.
Here's how my program works: The user inputs a name, gender, and age. The user can input as many data as they want. Now, the user searches a name; and the gender, and age of that name will display. The user is then prompted to edit the age. After inputting a new age, the file will now be edited, with the new age written.
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct data
{
char name[30];
char gender[10];
int age;
};
int main(void)
{
struct data client;
FILE* fp;
char ch = 0;
do
{
printf("Enter Name: ");
scanf("%s", client.name);
printf("Enter Gender: ");
scanf("%s", client.gender);
printf("Enter Age: ");
scanf("%d", &client.age);
fp = fopen("data.txt", "ab");
fwrite(&client, sizeof(client), 1, fp);
fclose(fp);
printf("continue? \n");
scanf(" %c", &ch);
} while (ch != 'n'); // continuously appends file till letter n is read;
char input[30]; // user input
printf("name?\n");
scanf("%s", input);
struct data Read;
fp = fopen("data.txt", "rb");
int newAge;
while (fread(&client, sizeof(client), 1, fp))
{
if (strcmp(client.name, input) == 0) // compare variable with user input
{
printf("%s", client.name);
printf(" %s", client.gender);
printf(" %d y/o", client.age);
printf("\n");
printf("enter new age");
scanf("%d", &newAge);
//fseek function
}
}
return 0;
}
Hope somebody can help.

How to read a specific line from binary file into a structure C

Essentially, I printed to a binary file using fseek() and fwrite(). However, I want to read the contents from a specific line into a structure. I also used fseek() and fread() to obtain the contents. I prompted the user to enter a code. From what I have learnt, I would use the value obtained from the user to use in the fseek function to get the specific line to start read from. Apparently, the fseek to read the contents does not work, I am getting gibberish essentially when it is displayed on the screen. Assistance is greatly appreciated.
#include <conio.h>
#include <stdio.h>
typedef struct registered
{
int compcode;
char compname[20];
int pinID;
int custID;
char IDtype[15];
int compID;
}REGISTERED;
void AddUpdate(REGISTERED info);
void SellPetrol();
void main(){
REGISTERED info = {0, "Apple", 0, 0, "passport", 0};
REGISTERED list;
AddUpdate(info);
SellPetrol();
}
void AddUpdate(REGISTERED info){
int choice;
FILE *registryfile = NULL;
registryfile = fopen("Sales.dat", "ab");
if (registryfile == NULL){
perror("Error: ");
}
else{
do{
printf("Company Code: ");
scanf("%d", &info.compcode);
printf("Company Name: ");
scanf("%s", &info.compname);
printf("Pin: ");
scanf("%d", &info.pinID);
printf("Customer ID: ");
scanf("%d", &info.custID);
printf("ID type: ");
scanf("%s", &info.IDtype);
printf("Company ID: ");
scanf("%d", &info.compID);
fseek(registryfile, (info.compcode - 1) * sizeof(REGISTERED), SEEK_SET);
fwrite(&info, sizeof(REGISTERED), 1, registryfile);
printf("Enter choice: ");
scanf("%d", &choice);
}while(choice == 1);
}
printf("\tCompany Code: %d\t\n", info.compcode);
printf("\tCustomer ID: %d\t\n", info.custID);
fclose(registryfile);
}
void SellPetrol(){
int code = 0, PIN;
REGISTERED list;
FILE *registryfile = NULL;
registryfile = fopen("Sales.dat", "rb");
if (registryfile == NULL){
perror("Error: ");
}
else{
printf("Please enter the company code: ");
scanf("%d", &code);
// printf("Please enter the PIN: ");
// scanf("%d", &PIN);
rewind(registryfile);
fseek(registryfile, (code - 1) * sizeof(REGISTERED), SEEK_SET);
fread(&list, sizeof(REGISTERED), 1, registryfile); //reads data into list
fflush(stdin);
printf("Company Code: %d\n", list.compcode);
printf("Company Name: %s\n", list.compname);
printf("Pin: %d\n", list.pinID);
printf("Customer ID: %d\n", list.custID);
printf("ID Type: %s\n", list.IDtype);
printf("Company ID: %d\n", list.compID);
}
fclose(registryfile);
}
It seems whichever method you're using to learn C is causing troubles, as the mistakes you seem to be making are common. I suggest reading a book, such as K&R2E... Do the exercises as you stumble across them; don't move on until you've completed them, and ask questions about them if necessary.
Don't fflush(stdin). fflush doesn't do what you think it does.
Check return values for functions such as fopen, scanf, fseek, fread, even fwrite. You'll probably find that your fread or scanf is returning a value indicating failure, hence the gibberish you speak of.
Be aware that C uses pass-by-value semantics ONLY. The source of at least one error in your code is a misunderstanding regarding these semantics. Namely, AddUpdate has no way to modify the variable declared within main, as it recieves a copy of that variable; at this point it seems void AddUpdate(REGISTERED info) should be void AddUpdate(void) and info should be declared within AddUpdate.
scanf("%s", &info.compname); probably doesn't do what you think it does. The %s directive tells scanf to read (metalinguistically speaking) a word (that is, a whitespace-delimitered token), not a line (a newline delimitered token), of user input. You probably want int x = scanf("%19[^\n]", info.compname); or better yet, char *x = fgets(info.compname, sizeof info.compname, stdin);...
void main() is unportable, and so is #include <conio.h>. You probably want int main(void) and ... you don't appear to be using any functions from <conio.h>, so you probably don't want anything in place of that. In C99, a main function that has no return statement will implicitly return 0; without a warning issued.

Error while assigning values to a struct using pointers

I am new to C and have been trying to get this simple code run which makes use of pointers to struct for calculating the average of grades entered. After entering the maths grade, the program throws an error and stops. What am I doing wrong.
Its also my first post in here, so please bear with me for any inconsistencies. Thanks!
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
char *name;
int mathGrade,scienceGrade,historyGrade,englishGrade;
}reportCard;
void average(reportCard *rc)
{
int avg = (rc->mathGrade +rc->scienceGrade+rc->historyGrade+rc->englishGrade)/4;
printf("The overall grade of %s is: %i ",rc->name, avg);
}
int main()
{
reportCard rc;
printf("Enter the Students Last name: ");
char studentName[20];
scanf("%s", studentName);
rc.name=studentName;
printf("Math Grade: \n");
scanf("%i", rc.mathGrade);
printf("Science Grade: \n");
scanf("%i", rc.scienceGrade);
printf("History Grade: \n");
scanf("%i", rc.historyGrade);
printf("English Grade: \n");
scanf("%i", rc.englishGrade);
average(&rc);
return 0;
}
You get an error because reading primitives with scanf requires pointers:
scanf("%i", &rc.mathGrade);
scanf("%i", &rc.scienceGrade);
// ^
// |
// Here
// ...and so on
scanf thinks that an uninitialized int that you pass is a pointer, and tries to write it, which results in an error.
In addition, you need to protect against buffer overruns on reading strings, like this:
scanf("%19s", studentName); // you allocated 20 chars; you need one for null terminator

Resources