Need help in C programming linked list - c

I'm creating a program using linked list which will later store in a text file. Basically this program will create an inventory, later update it as well. So I've successfully created the inventory (a function) and I've problem with updating it.
The inventory function is asking user to key in inputs, save into a text file and display it. The updating function has to select a particular item first and after that choosing whether to add or subtract the amount and the text file has to be updated at the same time.
So my problem is, I'm not able to compare the string input from the inventory function with this update function string input. Also, I need to receive an amount of item from user and then add the amount to the existing amount which I had entered in the inventory function earlier.
Visual Studio shows me that String 'code' might not be zero-terminated and String 'update' might not be zero-terminated for the update function.
I'm a beginner so I really need help from all of you, thank you!
This is the linked list codes:
struct donations
{
char supplyName[100], supplyCode[20], donator[150];
int no_ofShipment;
float quantityReceived;
struct donations* ptr;
} *start, *curr, *temp;
This is the inventory function (not a main function and will be changed later its a function for another main menu as the update function)
void main()
{
void details();
int i;
FILE *fp;
start = curr = NULL;
curr = start;
if (start == NULL)
{
start = curr = (struct donations *)malloc(sizeof(struct donations));
for (i = 0; i < 5; i++)
{
curr = (struct donations *)malloc(sizeof(struct donations));
details();
if (i == 0)
start = temp = curr;
else
{
temp->ptr = curr;
temp = curr;
}
}
temp->ptr = NULL;
temp = start;
printf("\n\t Inventory Created Successfully!");
printf("\n\t Recorded to the end of the list!");
}
//create file & print output into the file
if (fopen_s(&fp, "Donation.txt", "w") != 0)
{
printf("\nError");
return;
}
if (!fp)
{
printf("\n Error in opening file!");
//_getch();
return;
}
curr = start;
while (curr)
{
fprintf(fp, "%-30s", curr->supplyName);
fprintf(fp, "%-30s", curr->supplyCode);
fprintf(fp, "%-30s", curr->donator);
fprintf(fp, "%10d", curr->no_ofShipment);
fprintf(fp, "\t%.1f\n", curr->quantityReceived);
curr = curr->ptr;
}
printf("\n\n\t File (records) has been created!");
fclose(fp);
// display output
printf("\n\n\n\tDisplay Inventory");
while (temp)
{
printf("\n\n\n\t Name of Supply: %-30s", temp->supplyName);
printf("Supply Code: %-30s", temp->supplyCode);
printf("Donator: %-30s", temp->donator);
printf("No. of Shipment: %10d", temp->no_ofShipment);
printf("\tQuantity Received: %.1f", temp->quantityReceived);
temp = temp->ptr;
}
}
void details()
{
int i = 0;
system("cls");
printf("\tInventory Creation\t");
printf("\n\n\tEnter Name of Supply: ",(i+1));
gets_s(curr->supplyName);
while (getchar() != '\n');
printf("\n\tEnter Supply Code: ", (i + 1));
gets_s(curr->supplyCode);
while (getchar() != '\n');
printf("\n\tEnter Donator: ", (i + 1));
gets_s(curr->donator);
while (getchar() != '\n');
printf("\n\tEnter No. of Shipment: ", (i + 1));
scanf_s("%d", &curr->no_ofShipment);
while (getchar() != '\n');
printf("\n\tEnter Quantity Received: ", (i + 1));
scanf_s("%f", &curr->quantityReceived);
while (getchar() != '\n');
}
this is my update function, but not finished yet.
void update_DonationQuantity()
{
char code[10];
char update[5];
float quantity, Quantity;
FILE *fp;
printf("\n\tUpdate Donation Quantity\t\n");
printf("\n\n\tSelect donation (CT/HS/FM/SM/OM): ");
gets_s(code);
if (strcmp(code, curr->supplyCode) == 0)
{
printf("\n\tReceived/Distibuted item (+/-): ");
gets_s(update);
while (strcmp(update, "+") && (update, "-") != 0)
{
printf("\n\tWrong input, Try Again");
printf("\n\tReceived/Distibuted item (+/-): ");
gets_s(update);
}
if (strcmp(update, "+") == 0)
{
printf("\n\t Enter received amount: ");
scanf_s("%f", &quantity);
if (fopen_s(&fp, "Donation.txt", "a") != 0)
{
printf("\nError");
return;
}
if (!fp)
{
printf("\n Error in opening file!");
//_getch();
return;
}
curr->quantityReceived = quantity + curr->quantityReceived;
fprintf(fp, "\t%.1f\n", curr->quantityReceived);
}
}
}

There are multiple problems in your code:
struct donations *ptr; is confusing: linked lists usually have a next member to chain the subsequent list element.
void main() is incorrect: the return type of main is int.
void details(); defining a function in a local scope, while allowed is very bad style. You should move this declaration before the start of the main function.
if (start == NULL) is always true.
start = curr = (struct donations *)malloc(sizeof(struct donations)); This line is useless and causes a memory leak: both curr and start are overwritten in the for loop just below.
if (!fp) is redundant: if fopen_s failed, it would have returned 0, otherwise fp must have been set to a valid pointer. Using fopen instead seems simpler and more portable.
printf("\n\n\tEnter Name of Supply: ",(i+1)); the (i+1) argument is useless. The same problem appears in the next printf statements.
gets_s(curr->supplyName); should have a size argument. You might be compiling as C++ for the compiler to accept this. Microsoft has a non-standard template with the same name as the C so call safe function gets_s they forcefully introduced into the C Standard. This is utmostly confusing. You should use fgets(), scanf() or a custom made function to read a string into the destination array, consuming the excess characters and the newline if present. gets_s does not do that, it behavior if the line entered is too long is obscure... The return value should be checked for errors.
while (getchar() != '\n'); is risky: if the stream reaches the end of file before reading a newline, this loop will get stuck in an infinite loop. Furthermore gets_s() should have read the newline, so this loop is only useful for the %d and %f conversions. For this case you could use a flush_stdin() function written this way:
int flush_stdin(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}

Related

How to compare struct fields to variables in C? [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed 1 year ago.
This is part of a deletion function in a linked list program. I'm trying to compare the fields of the current struct node to fields read in by the user for searching. I know my node referencing works as I can print out current fields on their own, but my comparisons in the if statement to the variables does not. How can I compare the fields of current to the user data? Thanks
int deleteEmployee(void)
{
char name[MAX_NAME_LENGTH];
char gender[2];
int age;
char position[MAX_JOB_LENGTH];
int placeInList = 0;
Employee *current = employeeListHead;
Employee *previous = employeeListHead;
printf("Enter details of employee to delete: \n");
printf("Name: \n");
scanf(" %100[^\n]s", name);
scanf("%*[^\n]%*c");
printf("Gender: \n");
scanf(" %1s", gender);
scanf("%*[^\n]%*c");
printf("Age: \n");
scanf(" %d", &age);
scanf("%*[^\n]%*c");
printf("Title: \n");
scanf(" %100[^\n]s", position);
scanf("%*[^\n]%*c");
//while elements in list to search
while(current != NULL)
{
This Specifically
//handling a match on each iteration
if (current->name == name && current->gender == gender && current->age == age && current->position == position)
{
printf("\nIs this the emplyee you'd like to delete? Please confirm (Y/N) %s %s %d %s \n\n", name, gender, age, position);
char choice;
scanf(" %c", &choice);
//if delete is confirmed
if (choice == 'Y' || choice == 'y')
{
//if head of list
if(current == employeeListHead)
{
employeeListHead = current->next;
current = NULL;
return EXIT_SUCCESS;
}
//if tail
else if(current->next == NULL)
{
//change previous nodes pointer
for (int i=0; current!=NULL && i < placeInList-1; i++)
{
previous->next = NULL;
}
current = NULL;
return EXIT_SUCCESS;
}
//if inside list
else
{
for (int i=0; current!=NULL && i < placeInList-1; i++)
{
previous->next = current->next;
}
current = NULL;
return EXIT_SUCCESS;
}
}//end if yes selected to delete
//if delete is confirmed
if (choice == 'N' || choice == 'n')
{
printf("You selected N. Returning to main menu.");
EXIT_FAILURE;
}//end if no selected to delete
}//end if a match
//iterate to next set of nodes
current = current->next;
previous = previous->next;
placeInList++;
}//end iterating through list
printf("Employee not found in system.\n");
return EXIT_FAILURE;
}
You need to use strcmp to compare strings. When you write current->name == name, your program is just comparing the location of two pointers. (But you didn't show the definition of name, so it's hard to be sure.)
Try replacing all string comparisons with something like:
0 == strcmp(current->name, name)

Segmentation Fault from printing a linked list after reading from a file

I've almost completed my C program to add, view, save and load patient details. I've completed the add, save and load but can't seem to get the save function implemented correctly.
The program will allow users to add patient details, then save the details into a text file database. Users can then exit from the program, start the program and load the text file database. Finally, users will be able to see the patient details in the program.
In my code, I've also added some code to print out the contents after reading the text file, which works, printing out all the patient details onto the terminal. However, after I read from the text file, I try to use the view patient function and get a segmentation fault. Any help would be appreciated. Thanks!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define DB_NAME "database"
struct dob
{
int day, month, year;
};
typedef struct dob dob_t;
struct medicine
{
int medicine_id;
char medicine_name[100];
};
typedef struct medicine medicine_t;
struct patient
{
int patient_id;
dob_t date_db;
char patient_name[20];
medicine_t patient_med;
struct patient* nextp;
};
typedef struct patient patient_t;
void print_menu (void);
patient_t* add_patients (patient_t* patient_headp, patient_t* temp, patient_t* patient_currentp, int num_patients);
void view_patients (patient_t* patient_currentp, patient_t* patient_headp);
void save_patients (patient_t* patient_currentp, patient_t* patient_headp);
patient_t* read_patients (patient_t* patient_currentp, patient_t* patient_headp, patient_t* temp);
int main (void){
patient_t* patient_headp = NULL;
patient_t* temp = NULL;
patient_t* patient_currentp = NULL;
int option_picked = 0;
int num_patients = 0;
while(option_picked != 5)
{
print_menu ();
scanf("%d", &option_picked);
if (option_picked == 1){
patient_headp = add_patients(patient_headp, temp, patient_currentp, num_patients);
}
else if (option_picked == 2){
view_patients (patient_currentp, patient_headp);
}
else if (option_picked == 3){
save_patients (patient_currentp, patient_headp);
}
else if (option_picked == 4){
patient_headp = read_patients (patient_currentp, patient_headp, temp);
}
}
return 0;
}
void print_menu (void)
{
printf("\n"
"1. add a patient\n"
"2. display all patients\n"
"3. save the patients to the database file\n"
"4. load the patients from the database file\n"
"5. exit the program\n"
"Enter choice (number between 1-5)>\n");
}
patient_t* add_patients (patient_t* patient_headp, patient_t* temp, patient_t* patient_currentp, int num_patients){
char choice;
do
{
temp = (patient_t*) malloc(sizeof(patient_t));
if (temp == NULL){
printf("Error allocating memory\n");
}
printf("Enter Patient ID: ");
scanf("%d", &temp->patient_id);
printf("Enter Patient DOB(DD MM YY): ");
scanf("%d %d %d", &temp->date_db.day, &temp->date_db.month,
&temp->date_db.year);
printf("Enter Patient Name: ");
scanf("%s", temp->patient_name);
printf("Enter Patient Medicine Prescription: ");
scanf("%s", temp->patient_med.medicine_name);
printf("Enter Patient Medicine Prescription ID: ");
scanf("%d", &temp->patient_med.medicine_id);
temp->nextp = NULL;
if(patient_headp == NULL){
patient_headp = temp;
}
else{
patient_currentp = patient_headp;
while(patient_currentp->nextp != NULL){
patient_currentp = patient_currentp->nextp;
}
patient_currentp->nextp = temp;
}
printf("Add more patients? (Y/N) ");
scanf(" %c", &choice);
num_patients++;
}
while (choice == 'Y');
return patient_headp;
}
void view_patients (patient_t* patient_currentp, patient_t* patient_headp){
/*patient_currentp = (patient_t*) malloc(sizeof(patient_t));
if (patient_currentp == NULL){
printf("Error allocating memory\n");
}
patient_currentp = patient_headp;
do{
printf("%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id,
patient_currentp->date_db.day, patient_currentp->date_db.month,
patient_currentp->date_db.year, patient_currentp->patient_name,
patient_currentp->patient_med.medicine_name,
patient_currentp->patient_med.medicine_id);
patient_currentp = patient_currentp->nextp;
}while(patient_currentp->nextp != NULL);*/
printf("%05d %02d/%02d/%02d %s %s %d\n", patient_headp->patient_id,
patient_headp->date_db.day, patient_headp->date_db.month,
patient_headp->date_db.year, patient_headp->patient_name,
patient_headp->patient_med.medicine_name,
patient_headp->patient_med.medicine_id);
}
void save_patients (patient_t* patient_currentp, patient_t* patient_headp){
FILE *output = fopen(DB_NAME, "a");
if (output == NULL){
printf("Failed to open file\n");
}
patient_currentp = patient_headp;
do{
fprintf(output, "%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id,
patient_currentp->date_db.day, patient_currentp->date_db.month,
patient_currentp->date_db.year, patient_currentp->patient_name,
patient_currentp->patient_med.medicine_name,
patient_currentp->patient_med.medicine_id);
patient_currentp = patient_currentp->nextp;
}while(patient_currentp != NULL);
fclose(output);
}
patient_t* read_patients (patient_t* patient_currentp, patient_t* patient_headp, patient_t* temp){
FILE *input = fopen(DB_NAME, "r");
if (input == NULL){
printf("Failed to open file\n");
}
do{
temp = (patient_t*) malloc(sizeof(patient_t));
if (temp == NULL){
printf("Error allocating memory\n");
}
while ((fscanf(input, "%05d %02d/%02d/%02d %s %s %d",
&temp->patient_id, &temp->date_db.day,
&temp->date_db.month, &temp->date_db.year,
temp->patient_name,
temp->patient_med.medicine_name,
&temp->patient_med.medicine_id)) != EOF)
printf("%05d %02d/%02d/%02d %s %s %d\n", temp->patient_id,
temp->date_db.day, temp->date_db.month,
temp->date_db.year, temp->patient_name,
temp->patient_med.medicine_name,
temp->patient_med.medicine_id);
temp->nextp = NULL;
if(patient_headp == NULL){
patient_headp = temp;
}
else{
patient_currentp = patient_headp;
while(patient_currentp->nextp != NULL){
patient_currentp = patient_currentp->nextp;
}
patient_currentp->nextp = temp;
}
}while(patient_currentp != NULL);
return patient_headp;
}
The reality of your problem is (1) without input validation there are so many potential sources of error that could lead to Undefined Behavior and a Segmentation Fault that it is difficult to pin down (2) any time the address of the list could change in a function (e.g. the first node changes), you need to pass the address of patient_headp so that the function receives the actual list pointer instead a copy of a pointer holding the list address, and (3) your read_patients() is non-functional (for a number of reasons), but fundamentally because setting patient_currentp = patient_currentp->nextp; guarantees while(patient_currentp != NULL); test false.
There is no reason to pass patient_currentp as a parameter. There is no reason to pass num_patients in its current form, the parameter is unused, and for it to be useful you would need to pass a pointer to num_patients so you could update it within your add and read functions and make the updated counts available back in the calling function.
Before we even look at code, taking user input with scanf is wrought with pitfalls for the unwary in the event of a matching or input failure. To even begin to use scanf correctly you must validate the return every time. This means handling EOF, a matching or input failure, and handling the valid input case. At minimum, you must check that the anticipated number of conversions took place before making use of the input.
In the event of a matching failure, character extraction from the input buffer stops, leaving the offending character unread just waiting to bite you on your next attempt to read. To facilitate recovering from a matching failure, it is up to you to removed the offending characters from the input buffer. The normal approach for stdin is simply reading with getchar() until '\n' or EOF is encountered. A short helper-function makes life easier, e.g.
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
In addition to the validation issue, you may find it more robust to indicate success/failure in your add_patients() function by returning a pointer to the node added (or NULL on failure). This is somewhat complicated by your looping within the function to add multiple patients rather than simply calling the function again from the menu. Regardless, returning a pointer to the last node added works equally well.
There is no way possible to step through every issue in your code in the characters allotted for this answer. Instead, I've tidied up your code in a way that addresses each user-input validation, removes unnecessary parameters from your function declarations, and changed the return types for save_patients() and read_patients() to int to provide 1 on successful write or read, 0 otherwise.
(note the validation of fclose in save_patients(). Any time you are writing to a file, you should validate fclose as it will catch stream errors as well as errors with your last write that may not have been capable of reporting until close)
The code follows your approach, it has just been refined in several places. Look it over:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define DB_NAME "database"
#define MAXRX 100 /* if you need constants, #define on (or more) */
#define MAXNM 20 /* (don't use "magic numbers" in your code ) */
typedef struct {
int day, month, year;
} dob_t;
typedef struct {
int medicine_id;
char medicine_name[MAXRX];
} medicine_t;
typedef struct patient {
int patient_id;
dob_t date_db;
char patient_name[MAXNM];
medicine_t patient_med;
struct patient* nextp;
} patient_t;
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
void print_menu (void);
patient_t *add_patients (patient_t **patient_headp, int *num_patients);
void view_patients (patient_t *patient_headp);
int save_patients (patient_t *patient_headp);
int read_patients (patient_t **patient_headp, int *num_patients);
int main (void) {
patient_t *patient_headp = NULL;
int option_picked = 0,
num_patients = 0;
while(option_picked != 5)
{
print_menu ();
if (scanf("%d", &option_picked) != 1) { /* VALIDATE EVERY USER INPUT */
fputs ("\n error: invalid input.\n", stderr);
empty_stdin();
continue;
}
if (option_picked == 1)
add_patients (&patient_headp, &num_patients);
else if (option_picked == 2)
view_patients (patient_headp);
else if (option_picked == 3)
save_patients (patient_headp);
else if (option_picked == 4)
read_patients (&patient_headp, &num_patients);
}
return 0;
}
void print_menu (void)
{
printf ("\n"
"1. add a patient\n"
"2. display all patients\n"
"3. save the patients to the database file\n"
"4. load the patients from the database file\n"
"5. exit the program\n\n"
"Enter choice (number between 1-5)> ");
}
patient_t *add_patients (patient_t **patient_headp, int *num_patients)
{
patient_t *patient_currentp = *patient_headp,
*temp = NULL;
char choice = 0;
do
{
temp = malloc (sizeof *temp); /* allocate */
if (temp == NULL){ /* validate */
perror ("add_patients-malloc");
return NULL;
}
temp->nextp = NULL; /* initialize */
printf ("Enter Patient ID: ");
if (scanf ("%d", &temp->patient_id) != 1)
goto error_add_pt;
printf ("Enter Patient DOB(DD MM YY): ");
if (scanf ("%d %d %d", &temp->date_db.day, &temp->date_db.month,
&temp->date_db.year) != 3)
goto error_add_pt;
printf ("Enter Patient Name: ");
if (scanf ("%s", temp->patient_name) != 1)
goto error_add_pt;
printf ("Enter Patient Medicine Prescription: ");
if (scanf ("%s", temp->patient_med.medicine_name) != 1)
goto error_add_pt;
printf ("Enter Patient Medicine Prescription ID: ");
if (scanf ("%d", &temp->patient_med.medicine_id) != 1)
goto error_add_pt;
if (*patient_headp == NULL){
*patient_headp = patient_currentp = temp;
}
else {
while (patient_currentp->nextp != NULL){
patient_currentp = patient_currentp->nextp;
}
patient_currentp->nextp = temp;
}
(*num_patients)++;
printf ("Add more patients? (Y/N) ");
if (scanf (" %c", &choice) < 1) {
fputs (" user canceled input.\n", stderr);
break;
}
}
while (choice == 'Y' || choice == 'y');
return temp; /* return pointer to most recent node added */
error_add_pt:;
fputs ("error: invalid input\n", stderr);
empty_stdin();
free (temp);
return NULL;
}
void view_patients (patient_t *patient_headp)
{
patient_t *patient_currentp = patient_headp;
while (patient_currentp != NULL) {
printf ("%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id,
patient_currentp->date_db.day, patient_currentp->date_db.month,
patient_currentp->date_db.year, patient_currentp->patient_name,
patient_currentp->patient_med.medicine_name,
patient_currentp->patient_med.medicine_id);
patient_currentp = patient_currentp->nextp;
}
}
int save_patients (patient_t *patient_headp)
{
patient_t *patient_currentp = patient_headp;
FILE *output = fopen(DB_NAME, "a");
if (output == NULL) { /* validate file open to append */
fprintf (stderr, "error: file open failed '%s'\n", DB_NAME);
return 0;
}
while(patient_currentp != NULL) {
fprintf (output, "%05d %02d/%02d/%02d %s %s %d\n",
patient_currentp->patient_id,
patient_currentp->date_db.day, patient_currentp->date_db.month,
patient_currentp->date_db.year, patient_currentp->patient_name,
patient_currentp->patient_med.medicine_name,
patient_currentp->patient_med.medicine_id);
patient_currentp = patient_currentp->nextp;
}
if (fclose (output) == EOF) {
fputs ("error: stream error on fclose.\n", stderr);
return 0;
}
return 1;
}
int read_patients (patient_t **patient_headp, int *num_patients)
{
patient_t tmp = {0},
*patient_currentp = *patient_headp;
FILE *input = fopen(DB_NAME, "r");
if (input == NULL){ /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'\n", DB_NAME);
return 0;
}
while (patient_currentp && patient_currentp->nextp != NULL)
patient_currentp = patient_currentp->nextp;
while (fscanf (input, "%05d %02d/%02d/%02d %19s %99s %d",
&tmp.patient_id, &tmp.date_db.day,
&tmp.date_db.month,
&tmp.date_db.year,
tmp.patient_name,
tmp.patient_med.medicine_name,
&tmp.patient_med.medicine_id) == 7) {
patient_t *node = malloc (sizeof *node);
if (node == NULL) {
perror ("read_patients-malloc");
return 0;
}
node->nextp = NULL;
*node = tmp;
if (!patient_currentp)
*patient_headp = patient_currentp = node;
else {
patient_currentp->nextp = node;
patient_currentp = patient_currentp->nextp;
}
(*num_patients)++;
printf ("%05d %02d/%02d/%02d %s %s %d\n", node->patient_id,
node->date_db.day, node->date_db.month,
node->date_db.year, node->patient_name,
node->patient_med.medicine_name,
node->patient_med.medicine_id);
}
fclose (input);
return 1;
}
(note: your repeated allocation for patient_currentp in read_patients() was leaking memory and overwriting the pointer values you had previously assigned to your list. That is why an additional node variable is used)
Example Use/Output - Entering Data
$ ./bin/llpatients
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 1
Enter Patient ID: 10001
Enter Patient DOB(DD MM YY): 1 1 72
Enter Patient Name: Epoch
Enter Patient Medicine Prescription: Clonapin
Enter Patient Medicine Prescription ID: 2001
Add more patients? (Y/N) y
Enter Patient ID: 10002
Enter Patient DOB(DD MM YY): 31 10 72
Enter Patient Name: Halloween
Enter Patient Medicine Prescription: Potion
Enter Patient Medicine Prescription ID: 2002
Add more patients? (Y/N) n
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 2
10001 01/01/72 Epoch Clonapin 2001
10002 31/10/72 Halloween Potion 2002
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 3
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 5
Example Use/Output - Reading From File
$ ./bin/llpatients
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 4
10001 01/01/72 Epoch Clonapin 2001
10002 31/10/72 Halloween Potion 2002
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 2
10001 01/01/72 Epoch Clonapin 2001
10002 31/10/72 Halloween Potion 2002
1. add a patient
2. display all patients
3. save the patients to the database file
4. load the patients from the database file
5. exit the program
Enter choice (number between 1-5)> 5
Again, look things over, understand why the changes that were made, were made, and ask if you have any further questions.
in add_patients the following happens
Create Node
/* Create a temporary node */
temp = (patient_t*) malloc(sizeof(patient_t));
Fill Node with what ever Data
/* Copy Data to temporary Node */
printf("Enter Patient ID: ");
..
..
temp->nextp = NULL;
The linked list alterations
if(patient_headp == NULL){
patient_headp = temp;
}
else{
patient_currentp = patient_headp;
while(patient_currentp->nextp != NULL){
patient_currentp = patient_currentp->nextp;
}
patient_currentp->nextp = temp;
}
at this point temp is still pointing to the allocated memory so does linked list
and then this
free(temp);
results in segfault. so remove the free

How to stop reading a file once a certain string appears in c

I'm having trouble figuring out how to get my program to stop reading a file once the character string endOfFileMarker "***" is read given a file called "studentRecords.txt" with sample input as shown:
23456770,Mina,Porter,3,ENEE,114,CMSC,412,ENME,515
23456790,Alex,Simpson,1,CMSC,412
***
I'm reading the file using a while loop indicating that as long the file is not equal to the end of the file and if the first input from which I read is not equivalent to the endOfFileMarker. Right now the output doesn't stop reading at the endOfFileMarker and takes it as a new record in the structure with the given output of a display function (I realize the error with the 2nd record but that appears to be a problem with the display function and not the way I'm storing it):
23456770 Mina Porter 3 ENEE 114 CMSC 412 ENME 515
23456Alex Alex Simpson 1 CMSC 412
*** Alex Simpson 1 CMSC 412
I've tried using fgets earlier and creating an input buffer to read each line. But since there will be variable number of course names and course codes for each student, I found fscanf and using a while loop with control condition of !feof to work better. Kind of at a loss right now of how to stop storing into the structure once I hit the endOfFileMarker. If someone can please help me out with this, that would be very appreciated. My full code is written below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define filename "studentRecords.txt"
typedef struct courseInfo
{//structure defining courseInfo elements
int courseID;
char courseName[30];
}crsInfo;
typedef struct studentInfo
{//structure defining studentInfo elements
char studentID[9];
char firstName[20];
char lastName[25];
int coursesAttended;
crsInfo cInfo[10];
struct studentInfo * next;
}stdInfo;
stdInfo * firstStdNodePointer = NULL;
stdInfo * currentStdNodePointer = NULL;
void addStudentInfo(stdInfo newStd)
{
if (firstStdNodePointer == NULL) //Create the first course node
{
firstStdNodePointer = (stdInfo *) malloc(sizeof(stdInfo));
strcpy(firstStdNodePointer->studentID, newStd.studentID);
strcpy(firstStdNodePointer->firstName, newStd.firstName);
strcpy(firstStdNodePointer->lastName, newStd.lastName);
firstStdNodePointer->coursesAttended = newStd.coursesAttended;
for(int i = 0; i < newStd.coursesAttended; i++)
{
firstStdNodePointer->cInfo[i].courseID = newStd.cInfo[i].courseID;
strcpy(firstStdNodePointer->cInfo[i].courseName, newStd.cInfo[i].courseName);
}
firstStdNodePointer->next = NULL;
currentStdNodePointer = firstStdNodePointer;
}
else // add next course to the end of the course linked list.
{
// Go to the last Course in the list to get the course ID
stdInfo * newStdNodePointer = (stdInfo *) malloc(sizeof(stdInfo));
strcpy(newStdNodePointer->studentID, newStd.studentID);
strcpy(newStdNodePointer->firstName, newStd.firstName);
strcpy(newStdNodePointer->lastName, newStd.lastName);
newStdNodePointer->coursesAttended = newStd.coursesAttended;
for(int j = 0; j < newStd.coursesAttended; j++)
{
newStdNodePointer->cInfo[j].courseID = newStd.cInfo[j].courseID;
strcpy(newStdNodePointer->cInfo[j].courseName, newStd.cInfo[j].courseName);
}
newStdNodePointer->next = NULL;
currentStdNodePointer->next = newStdNodePointer; // Link previous node with newNode
currentStdNodePointer = currentStdNodePointer->next; // Make current node as previous node
}
}
void loadStudentInfo()
{
FILE * fptr = NULL;
fptr = fopen(filename, "r+");
const char endOfFileMarker[] = "***"; //marks the end of the student record list
if(fptr == NULL)
{
printf("File can not be opened\n");
}
stdInfo newStd;//defining a new struct studentInfo variable so I can pass to the addStudent function
//char line[100] = "";
//char * strPtr;
while (!feof(fptr) && strcmp(newStd.studentID, endOfFileMarker) != 0 )
{
fscanf(fptr, "%[^,],", newStd.studentID);
printf("%s\n", newStd.studentID);
fscanf(fptr, "%[^,],", newStd.firstName);
printf("%s\n", newStd.firstName);
fscanf(fptr, "%[^,],", newStd.lastName);
fscanf(fptr, "%i,", &newStd.coursesAttended);
for(int j = 0; j < newStd.coursesAttended; j++)
{//To read each courseName and ID, you need to go according to how many courses they entered
//because the amount of records in cInfo should correspond with how many pairs of courseName
//are entered into the file
fscanf(fptr, "%[^,],", newStd.cInfo[j].courseName);
fscanf(fptr, "%i,", &newStd.cInfo[j].courseID);
}
addStudentInfo(newStd);
}
fclose(fptr);
}
void displayCourseInfo()
{
printf("------------------------------------------------\n");
stdInfo * stdListPointer = firstStdNodePointer;
//start from the beginning
while(stdListPointer != NULL) {
printf("%s %s %s\t%i\t", stdListPointer->studentID, stdListPointer->firstName, stdListPointer->lastName, stdListPointer->coursesAttended);
for(int i = 0; i < stdListPointer->coursesAttended; i++)
{
printf(" %s %i ", stdListPointer->cInfo[i].courseName, stdListPointer->cInfo[i].courseID);
}
printf("\n");
stdListPointer = stdListPointer->next;
}
printf("------------------------------------------------\n");
}
void switchCaseMenu()
{
int selection;
int menuActive = 1;
while(menuActive)
{
printf("60-141 Bonus Assignment - Ben John\n");
printf("------------\n");
printf("1. Add a new student\n");
printf("2. Delete a student\n");
printf("3. Search for a student\n");
printf("4. Display current students\n");
printf("5. Save student information to file\n");
printf("6. Exit\n");
printf("Please enter a selection: ");
scanf("%i", &selection);
switch(selection)
{
case 1:
printf("~Selected - Add a new student~\n");
break;
case 2:
printf("~Selected - Delete a student~\n");
break;
case 3:
printf("~Selected - Search for s student~\n");
break;
case 4:
printf("~Selected - Display current students~\n");
displayCourseInfo();
break;
case 5:
printf("~Selected - Save student information to file~\n");
break;
case 6:
printf("~Selected - Exit~\n");
menuActive = 0;
break;
default:
printf("Invalid Input!\n");
}
}
printf("Goodbye!\n");
}
int main(void)
{
loadStudentInfo();
switchCaseMenu();
return 0;
}
I'll suggest that you read the file line by line using fgets and use sscanf to do the scanning. Then you can use strcmp to break the loop. Something like:
while(fgets(buffer, SIZE_OF_BUFFER, fileptr))
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') buffer[len - 1] = '\0'; // Strip \n if present
if (strcmp(buffer, "***") == 0) break; // Stop reading
// use sscanf on buffer to find the individual fields in the line
}
Note that fgets also stores the \n character (aka newline) into the buffer so before doing the string compare, the \n is stripped off (if present).
For your use case you don't really need to test whether the last character in the string is actually a \n. Just make the buffer sufficiently large and always strip off the last character. In this way the code can be simplified to:
while(fgets(buffer, SIZE_OF_BUFFER, fileptr))
{
size_t len = strlen(buffer);
if (len) buffer[len - 1] = '\0'; // Strip last character
if (strcmp(buffer, "***") == 0) break; // Stop reading
// use sscanf on buffer to find the individual fields in the line
}
or an even more compact way (thanks to #melpomene):
while(fgets(buffer, SIZE_OF_BUFFER, fileptr))
{
buffer[strcspn(buffer, "\n")] = '\0'; // Strip \n character if present
if (strcmp(buffer, "***") == 0) break; // Stop reading
// use sscanf on buffer to find the individual fields in the line
}

Why is my file output overwritten?

My program takes user input and stores it in an array of Records that I've defined as a structure: struct Record.
The user input is are the fields of the struct. Everything complies error free, but it seems I can't get the formatting correct. My program keeps asking for user input until the user enters 'n' when asked if there are anymore records.
Once there are no more records the program loops through the created records and file prints each of them spaced out by tabs and at the end starting with a newline for the next record. However, instead of starting at a new line and printing another record in the same fashion, it overwrites the previous record printed and tabs the next one even further.
What causes this to happen?
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
char zipcode[51];
char phoneNumber[51];
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = 'y';
int size = 1;
int i = 0;
struct Record *records;
struct Record *records_temp = NULL;
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = calloc((size),sizeof(*records));
records = records_temp;
printf("First Name: \n");
scanf("%s", records[size-1].fname);
printf("Last Name: \n");
scanf("%s", records[size-1].lname);
printf("Address: \n");
scanf(" %[^\n]", records[size-1].address);
printf("City: \n");
scanf("%s", records[size-1].city);
printf("State: \n");
scanf("%s", records[size-1].state);
printf("Zipcode: \n");
scanf("%s", records[size-1].zipcode);
printf("Phone Number: \n");
scanf("%s", records[size-1].phoneNumber);
//stores all record info
printf("Are there anymore records? [y/n] ");
scanf(" %c", &answer);
if(answer == 'y' || answer == 'Y')
{
size++;
printf("\n");
}
}
//open file
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL)
{
for(;i< size; i++)
{
fprintf(fileWriter,"%s\t",records[i].fname);
fprintf(fileWriter,"%s\t",records[i].lname);
fprintf(fileWriter,"%s\t",records[i].address);
fprintf(fileWriter,"%s\t",records[i].city);
fprintf(fileWriter,"%s\t",records[i].state);
fprintf(fileWriter,"%s\t",records[i].zipcode);
fprintf(fileWriter,"%s\n",records[i].phoneNumber);
}
free(records);
fclose(fileWriter);
}
else
{
printf("Error opening file.");
}
}
I changed a little bit your code, but I think you should use linked list as a data structure here, it's more simple and consume less memory.
I made some tries and all went ok. :)
Hope that help you!!
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct Record Record;
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
char zipcode[51];
char phoneNumber[51];
Record *next;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = '\0';
// int size = 1;
// int i = 0;
Record *records = NULL;
Record *records_first = NULL;
Record *records_previous = NULL;
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL) {
for( ; ; ) {
records = (Record*) malloc(sizeof(Record));
if(records_first == NULL)
records_first = records;
if(records_previous != NULL)
records_previous->next = records;
records = records_first;
printf("First Name: \n");
scanf("%s", records->fname);
fprintf(fileWriter,"%s\t",records->fname);
printf("Last Name: \n");
scanf("%s", records->lname);
fprintf(fileWriter,"%s\t",records->lname);
printf("Address: \n");
scanf(" %[^\n]", records->address);
fprintf(fileWriter,"%s\t",records->address);
printf("City: \n");
scanf("%s", records->city);
fprintf(fileWriter,"%s\t",records->city);
printf("State: \n");
scanf("%s", records->state);
fprintf(fileWriter,"%s\t",records->state);
printf("Zipcode: \n");
scanf("%s", records->zipcode);
fprintf(fileWriter,"%s\t",records->zipcode);
printf("Phone Number: \n");
scanf("%s", records->phoneNumber);
fprintf(fileWriter,"%s\t\n\n",records->phoneNumber);
records->next = NULL;
records_previous = records;
printf("Are there anymore records? [y/n] ");
scanf(" %c", &answer);
if(tolower(answer) != 'y') {
free(records);
fclose(fileWriter);
break;
}
}
} else
printf("Error opening file.");
return 0;
}
Open the file with additional a mode as in "append". I think there is no need for further explanation what that would be good for, right?
However, the actual problem seems that you overwrite the pointer to the previous record already in the input loop. How is that supposed to work? Just go through that loop and try to follow the instructions as the program would do.
Problem is you cannot do it like this. Use a linked list for the allocated blocks.
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = calloc((size),sizeof(*records));
records = records_temp;
...
}
Did you perhaps mean this?
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = realloc(records, size * sizeof *records);
if (records_temp == NULL)
{
/* Handle allocation error */
}
records = records_temp;
/* ... */
}
Do not confuse calloc with realloc. Read the manuals if you need further clarification.
Don't forget to initialise records to NULL...
If your concern is optimisation, the most significant bottleneck here will be your file input/output. That's unavoidable, and aside from researching setvbuf there isn't much you can do about it. The next bottleneck will be underlying calls to kernel allocation functions. You can reduce that bottleneck by calling allocation functions less. For example, you could grow your array by doubling its size each time rather than by adding 1:
size_t size = 0;
int answer;
do {
size_t index = size++;
if ((index & size) == 0) {
void *temp = realloc(array, (2 * index + 1) * sizeof *array);
if (temp == NULL) {
/* Handle allocation error */
}
array = temp;
}
puts("First Name:");
scanf("%s", array[index].fname);
/* snip */
answer = getchar();
} while (answer != EOF && tolower(answer) == 'y');
Alternatively, you could reinvent the wheel and perform the same work that realloc performs behind the scenes (and possibly lose some benefit of optimisation), by adding calls to memcpy and free to your code like so:
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = calloc((size),sizeof(*records));
if (records_temp == NULL)
{
/* Handle allocation error */
}
if (records != NULL)
{
memcpy(records_temp, records, (size - 1) * sizeof *records);
free(records);
}
records = records_temp;
/* ... */
}
P.S. In case you missed it the first time: Don't forget to initialise records to NULL...
The biggest problem I see is memory allocation logic. The first time through the loop, you allocate memory for 1 record and increment size. The second time through the loop, you allocate memory for 2 more records, because size == 2. The third time through the loop, you allocate memory for 3 more records, for a total of 1+2+3=6.
That answers why it's happening. Olaf's suggested fix, linked list, is a good one.

My program is asking too many times for input. C while loop

My program is asking too many times for input, after the first input. Source code:
#include <stdio.h>
#include <stdlib.h>
typedef struct data{
char name[100];
int n_in_class;
int grade;
struct data *p;
}data;
void add(data *begin);
int main(void){
char n;
data *begin = malloc(sizeof(data));
while(1 == 1){
printf("Do you want to add new student(n) or (y)");
n = getchar();
if(n == 'n'){
break;
}
else if(n == 'y'){
add(begin);
}
}
data *temp = begin;
while(temp != NULL){
printf("%s", temp->name);
temp = temp->p;
}
}
void add(data *begin){
data *new = malloc(sizeof(data));
printf("enter name: ");
scanf("%s", &new->name);
printf("Enter number in class: ");
scanf("%i", &new->n_in_class);
printf("enter grade: ");
scanf("%i", &new->grade);
data *temp = begin;
while(1==1){
if(temp->p == NULL){
temp->p = new;
break;
}
else{
temp = temp->p;
}
}
}
The program starts, I enter y and it's calling the add function, I enter all information without problems, after I finish with entering information and when it comes time to ask for input again its printing the message "Do you want to add new student(n) or (y)" twice. I have try with different functions for getting input scanf, fgets, getline and getchar.
When you input y or n and press ENTER, the new line character is also in the input buffer, you need to remove this newline character before the next loop.
if(n == 'n'){
break;
}
else if(n == 'y'){
add(begin);
}
getchar(); //consumes new line
To consume the newline in the input buffer,
you can also use
scanf(" %c", &n) ; - a space before the format descriptor(%c)
in place of the first getchar()
or
you can also use
getc(stdin)' in place of the second getchar() to achieve the same.

Resources