Save, find and remove structure from txt file - c

I am studying IT and programming is something new for me. Last week I got a task to do. I have to write a program which can save a structure (personal data of company's workers) to file.txt. Program should be able to find a specific person (putting his name or surname) and delete all people whose got a surname given by user as well. These all tasks should be in separate functions. Till now I wrote this function:
void struktura()
{
struct Person
{
char name[30];
char surname[30];
int age;
int height;
int weight;
};
int ile;
int i = 0;
printf("\n");
printf("How many persons would you like add to database (max 10 at once): ");
scanf("%d", &ile);
printf("\n");
if (ile >= 0)
{
printf("You added no one.\n");
}
else
{
struct Person *osoba[10]; //
while (i < ile)
{
osoba[i] = (struct Person*) malloc(sizeof (struct Person));
printf("Give a name: ");
scanf("%s", osoba[i]->name);
printf("Give a surname: ");
scanf("%s", osoba[i]->surname);
printf("Age: ");
scanf("%d", &osoba[i]->age);
printf("Height: ");
scanf("%d", &osoba[i]->height);
printf("Weight: ");
scanf("%d", &osoba[i]->weight);
printf("\n");
i = i + 1;
}
printf("You have added: \n\n");
i = 0;
while (i < ile)
{
printf("%d) Name: %s, Surname: %s, Age: %d years, Height: %d cm, Weight: %d kg.\n", i + 1, osoba[i]->name, osoba[i]->surname, osoba[i]->age, osoba[i]->height, osoba[i]->weight);
i = i + 1;
}
for (i = 0; i < ile; i++)
{
free(osoba[i]);
}
}
}
In additition, I have a code which save these persons to the file.txt. I would like to separate code (below) to another function but I don't know how to pass a structure to function.
FILE *save;
char name[30];
printf("\nWhere to save a database: ");
scanf("%s", name);
save = fopen(name, "a");
if (save == NULL)
{
printf("This file doesn't exist!");
getchar();
exit(1);
}
i = 0;
while (i < ile)
{
fprintf(save, "Name: %s, Surname: %s, Age: %d years, Height: %d cm, Weight: %d kg.\n",osoba[i]->name, osoba[i]->surname, osoba[i]->age, osoba[i]->height, osoba[i]->weight);
i = i + 1;
}
fclose(save);
I wrote a function, which opens the whole content of file.txt as well. What I missing is : how to find a specific person and how to delete these persons(I was thinking about opening a second temporary file and copy an content of original file except these lines which include given name/surname) (all in separate functions). Do you have any ideas how I could do this? I was looking for it in some books but I couldn't find any help.

I would like to separate code (below) to another function but I don't
know how to pass a structure to function.
Define struct Person outside and above the functions, so it can be used in all of them. Then you can put the code which saves the persons to the file in a function
void save(int ile, struct Person *osoba[ile])
{
…
}
and call this with
save(ile, osoba);
how to find a specific person
You could use the following function, which is a little more complicated than necessary for the finding task, but can be reused for another purpose:
char prformat[] =
"Name: %s, Surname: %s, Age: %d years, Height: %d cm, Weight: %d kg.\n";
char scformat[] =
"Name: %[^,], Surname: %[^,], Age:%d years, Height:%d cm, Weight:%d kg.\n";
// Read the data file "file.txt", look for 'name' or 'surname' (unless NULL),
// and output line(s) to 'out' if or unless they match (depending on 'match')
scan(char *name, char *surname, _Bool match, FILE *out)
{
FILE *fp = fopen("file.txt", "r");
if (!fp) perror("file.txt"), exit(1);
struct Person osoba;
while (fscanf(fp, scformat, osoba.name, osoba.surname,
&osoba.age, &osoba.height, &osoba.weight) == 5
)
if ( name && strcmp(osoba. name, name) == 0 == match
|| surname && strcmp(osoba.surname, surname) == 0 == match
)
if (fprintf(out, prformat, osoba.name, osoba.surname,
osoba.age, osoba.height, osoba.weight)
< 0) perror(""), exit(1);
fclose(fp);
}
calling it with
char name[30];
printf("Give a name or surname to find: ");
if (scanf("%29s", name) == 1)
scan(name, name, 1, stdout); // print matching lines to stdout
delete all people whose got a surname given by user …
(I was thinking about opening a second temporary file and copy an content of original file except these lines …
You were thinking right:
printf("Give a surname to delete: ");
if (scanf("%29s", name) == 1)
{
FILE *fp = fopen("file.tmp", "w");
if (!fp) perror("file.tmp"), exit(1);
scan(NULL, name, 0, fp); // print non-matching lines to tempfile
if (fclose(fp) < 0) perror("file.tmp"), exit(1);
remove("file.txt");
if (rename("file.tmp", "file.txt") != 0) perror("file.tmp");
}

Related

When I try update an element in a line my file, the wrong element get updated in C. How access each element in a line in a file?

I am creating a task management system where the user can add,modify and delete their tasks. But I am facing errors with modifying elements in a line in a file.
This is where I add tasks to the file:
void add_task()
{
int id;
char name[50];
char category[50];
char info[50];
char date[20];
char status[20];
char another = 'Y';
FILE *fptr;
fptr = fopen("Tasks.txt","r+");
if (fptr==NULL)
{
printf("\nSYSTEM ERROR");
fptr = fopen("Tasks.txt","w");
getchar();
return;
}
while (another == 'Y'|| another == 'y')
{
fseek(fptr,0,SEEK_END);
printf("\nTo Add a new task enter the details below\n");
printf("Enter an ID to identify your tasks easily(LESS THAN 5 CHAR):");
scanf("%d",&id);
printf("Name of Task:");
getchar();
fgets(name,50,stdin);
name[strcspn (name, "\n")] = 0;
printf("Category of Task:");
fgets(category,50,stdin);
category[strcspn (category, "\n")] = 0;
printf("Information about task(Max 49 characters):");
fgets(info, 50, stdin);
info[strcspn (info, "\n")] = 0;
printf("Due Date of Task(yyyy/mm/dd):");
scanf(" %s", date);
printf("Status of Task\n TD = To-Do\n IP = In Progress\n CT = Completed Task\nEnter Status:");
scanf(" %s", status);
fprintf(fptr,"%d %s %s %s %s %s\n",id, name, category, info, date, status);
printf("\nDo you want to add another record (Y/N)");
fflush(stdin);
another = getchar();
}
fclose(fptr);
printf("\n\n\tPress any key to exit");
exit(0);
}
This is where I want to change only the category of the task but it changes the name instead:
void change_category()
{
int id;
char name[50];
char category[50];
char info[50];
char date[20];
char status[20];
int i;
FILE * fptr = fopen("Tasks.txt","r");
FILE * fp = fopen("temp.txt","w");
printf("Enter the ID of the task you want to update:");
scanf("%d",&id);
while(fscanf(fptr,"%d",&i) != EOF)
{
fscanf(fptr,"%s",category);
fgets(info,50,fptr);
if(i == id)
{
printf("Task Found");
printf("\nUpdating category for task with ID: %d",id);
printf("\nEnter new category:");
scanf("%s",category);
}
fprintf(fp,"%d %s %s\n",i,category,info);
}
fclose(fp);
fclose(fptr);
remove("Tasks.txt");
rename("temp.txt","Tasks.txt");
}
Sample entry into the file:
8291 Math Math HW Pg 1-2 2022/04/03 IP
Update Category entry:
English
Updated File:
8291 English Math HW Pg 1-2 2022/04/03 IP

How to delete a record when there's more than one record of the same name in C

I am working on code for s blood donation system. I'm currently struggling with the delete donor record function.
The delete record function is to delete the record of a donor with a given name. If there are two or more records with the same name in the file then the program asks for mobile number. While there may be more than one person with the same name, each person has a unique mobile number.
My problem is that when the same name is used for several records, the wrong record is deleted.
If there's only one record with that name, the program deletes the record in the manner that's required.
(The variable i is declared globally as int i)
Here's the delete function
void delete(struct blood *b,int n)
{
char name[50];
int phone;
int found=0;
int c=0;
FILE *fp = fopen("bloodrecord.txt", "r");
FILE *fp1 = fopen("temp.txt", "w");
printf("\nEnter Name: ");
scanf("%s", name);
printf("---------------------------------------------\n");
while(fread(&b[i],sizeof(struct blood),1,fp))
{
if(strcmpi(b[i].name,name)==0)
{
c=c+1;
printf("\nName: %s\n",b[i].name);
printf("Age: %d\n", b[i].age);
printf("Mobile no.: %d\n", b[i].phone);
printf("Blood group: %s\n", b[i].bg );
printf("Weight: %d\n", b[i].weight);
printf("Sex: %s\n",b[i].sex);
printf("Address: %s\n",b[i].add);
printf("\n");
if (c==1)
{
found=1;
}
else if(c>1)
{
printf("\nThere are more than one occurences of this name in the records\n");
printf("\nPlease enter the mobile number of the donor: ");
scanf("%d", &phone);
if (b[i].phone == phone)
{
found=1;
}
}
}
else
fwrite(&b[i],sizeof(struct blood),1,fp1);
}
fclose(fp);
fclose(fp1);
if (found==1)
{
fp1 = fopen("temp.txt", "r");
fp = fopen("bloodrecord.txt", "w");
while(fread(&b[i],sizeof(struct blood),1,fp1))
{
fwrite(&b[i],sizeof(struct blood),1,fp);
}
fclose(fp);
fclose(fp1);
}
else
{
printf("\n\aRECORD DOES NOT EXIST.\n");
}
printf("RECORD SUCCESSFULLY DELETED");
getchar();
getchar();
}
I suggest that to make the program simpler, you request both the donor's name and the donor's mobile number at the beginning.
Then you process the input file and look for both name and mobile number in a single pass.
I started with your code and made a few changes. See comments.
Please note that I have not tested this code nor compiled it. It should be essentially correct however there may be a compiler error if I made a syntax mistake.
I assume that the you are using the struct blood correctly in your code since you did not provide the code defining that struct.
I assume that an int is sufficiently large to hold the mobile number. Since the size of an int can vary and is determined by the compiler, it may or may not be large enough for a mobile number. See Range of values in C Int and Long 32 - 64 bits
One thing I do not understand is why you are using the b[i] syntax and where is the variable i defined? You could instead use a local variable in the delete() function.
I also have the delete() function returning a value indicating if it found a match or not. This may or may not be useful.
int delete()
{
struct blood b;
char name[50] = {0};
int phone;
int found = 0;
FILE *fp = fopen("bloodrecord.txt", "r");
FILE *fp1 = fopen("temp.txt", "w");
// Ask for the donor's mobile number along with their name
// at the beginning to make the search easier and be able to
// do this in a single pass.
printf("\nEnter Name of the donor: ");
scanf("%49s", name); // Oka's comments about scanf().
printf("\nPlease enter the mobile number of the donor: ");
scanf("%d", &phone);
printf("---------------------------------------------\n");
while(fread(&b, sizeof(struct blood), 1, fp))
{
// check both donor's name and donor's mobile number.
if(strcmpi(b.name, name) == 0 && b.phone == phone)
{
// print out the donor data and indicate we are deleting
// this donor record.
printf("Deleting donor record\n");
printf(" Name: %s\n", b.name);
printf(" Age: %d\n", b.age);
printf(" Mobile no.: %d\n", b.phone);
printf(" Blood group: %s\n", b.bg );
printf(" Weight: %d\n", b.weight);
printf(" Sex: %s\n", b.sex);
printf(" Address: %s\n", b.add);
printf("\n");
found = 1;
}
else {
// we are keeping this donor record so write it to the
// temp file.
fwrite(&b, sizeof(struct blood), 1, fp1);
}
}
fclose(fp);
fclose(fp1);
if (found == 1)
{
// file temp.txt has deleted donors so lets updated
// the original file, bloodrecord.txt, with the updated
// list of donors.
fp1 = fopen("temp.txt", "r");
fp = fopen("bloodrecord.txt", "w");
while(fread(&b, sizeof(struct blood), 1, fp1))
{
fwrite(&b, sizeof(struct blood), 1, fp);
}
fclose(fp);
fclose(fp1);
printf("RECORD SUCCESSFULLY DELETED");
}
else
{
printf("\n\aRECORD DOES NOT EXIST.\n");
}
getchar();
getchar();
return found; // indicate if we found a match or not.
}
I reused most of your code, and added a second pass to handle the actual delete (first pass searches for matching records).
void delete(struct blood *b,int n)
{
const int MOBILE_SIZE = 16;
char name[50];
int phone = 0;
int found=0;
int c=0;
FILE *fp = fopen("bloodrecord.txt", "r");
FILE *fp1 = fopen("temp.txt", "w");
printf("\nEnter Name: ");
scanf("%s", name);
printf("---------------------------------------------\n");
while(fread(&b[i],sizeof(struct blood),1,fp))
{
if(strcmpi(b[i].name,name)==0)
{
c=c+1;
printf("\nName: %s\n",b[i].name);
printf("Age: %d\n", b[i].age);
printf("Mobile no.: %d\n", b[i].phone);
printf("Blood group: %s\n", b[i].bg );
printf("Weight: %d\n", b[i].weight);
printf("Sex: %s\n",b[i].sex);
printf("Address: %s\n",b[i].add);
printf("\n");
found = 1;
}
}
/* Finished the first pass. Now, start again */
rewind(fp)
if (c > 1) {
printf("There are multiple records for the name %s\n", name);
printf("\nEnter Mobile Number: ");
scanf("%d", &phone);
}
while(fread(&b[i],sizeof(struct blood),1,fp))
{
if((c == 1 && strcmpi(b[i].name,name)==0)
|| (c > 1 && strcmpi(b[i].name,name) == 0 && b[i].mobile == mobile))
continue; /* skip this record */
}
else
fwrite(&b[i],sizeof(struct blood),1,fp1);
}
fclose(fp);
fclose(fp1);
if (found==1)
{
fp1 = fopen("temp.txt", "r");
fp = fopen("bloodrecord.txt", "w");
while(fread(&b[i],sizeof(struct blood),1,fp1))
{
fwrite(&b[i],sizeof(struct blood),1,fp);
}
fclose(fp);
fclose(fp1);
}
else
{
printf("\n\aRECORD DOES NOT EXIST.\n");
}
printf("RECORD SUCCESSFULLY DELETED");
getchar();
getchar();
}

Passing array of structure to fread and fwrite function in C

I have a questions regarding the fread() and fwrite() function in c.
Talking about the first parameter void* If i am dealing with array of structure and want to pass an entire array what should i use &arrayName or arrayName.
Now I am trying to input some data from users create a student.txt file and store the data in that file and again read that data from the same file and display it.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
struct dob
{
int year,month,date;
};
struct student
{
int roll;
char name[20],address[20],faculty[30];
struct dob a;
};
struct student s1[2],s2[2];
FILE *fp;
fp = fopen("student.txt","w+b");
if(fp == NULL)
{
printf("Cannot open file");
exit(0);
}
for(int i = 0 ; i < 2 ; i++)
{
fflush(stdin);
printf("Enter the name of student %d\n",i+1);
scanf("%[^\n]s",s1[i].name);
fflush(stdin);
printf("Enter address\n");
scanf("%[^\n]s",s1[i].address);
fflush(stdin);
printf("Enter roll no\n");
scanf("%d",&s1[i].roll);
fflush(stdin);
printf("Enter faculty\n");
scanf("%[^\n]s",s1[i].faculty);
fflush(stdin);
printf("Enter the Birth year\n");
scanf("%d",&s1[i].a.year);
printf("Enter the Birth month\n");
scanf("%d",&s1[i].a.month);
printf("Enter the Birth date\n");
scanf("%d",&s1[i].a.date);
}
fwrite(&s1,sizeof(s1),2,fp);
rewind(fp);
fread(&s2,sizeof(s2),2,fp);
for(int i = 0 ; i < 2 ; i++)
{
printf("Name : %s\n Address = %s\n Faculty = %s\n Roll = %d\n year = %d\n month = %d\n date =
%d\n",
s2[i].name,s2[i].address,s2[i].faculty,s2[i].roll,s2[i].a.year,s2[i].a.month,s2[i].a.date);
}
fclose(fp);
return 0;
}
I am asking it because both of them works for my above code.But How?
I am talking about this line
fwrite(&s1,sizeof(s1),2,fp);
fread(&s2,sizeof(s2),2,fp);
Here s1 and s2 works without & as well

How to add strings and integers to an array then print to a text file

Goodday, when I get to entering the first name of the student, the program immediately crashes. Also I'm not exactly sure how to add the names and the ID into an array to print to the text file. May I have some assistance please?
struct records{
int id;
char fname[15];
char lname[15];
};
struct records student;
int max=1000;
int i;
srand( time(NULL) ); //random numbers generated
ATND= fopen("Student Record.txt","a");
if(ATND== NULL){
printf("ERROR!");
exit(1);
}
for(i=0; i<100; i++){
printf("Enter student\'s first name: ");
scanf("%s", student.fname[i]);
printf("\n\n");
printf("Enter student\'s last name: ");
scanf("%s", student.lname[i]);
/*randomnumber*/student.id[i]=rand() %max + 39048543;
fprintf(ATND,"%s %s %d", student.fname[i], student.lname[i], student.id[i]);
}
fclose(ATND);
Code only provides data space for 1 record whereas it appears to need 1000 records. Number of other issues. Suspect after 10 hours, OP has worked a number of them
//Definition - good
struct records {
int id;
char fname[15]; // IMO 15 is too limiting for first and last
char lname[15];
};
// Only 1 student, need many more
// struct records student;
#define STUDENT_N 1000
struct records student[STUDENT_N];
void read_records(void) {
// avoid magic numbers
// int max = 1000;
int max = STUDENT_N;
int i;
srand(time(NULL)); //random numbers generated
// ATND not declared
FILE *ATND;
ATND = fopen("Student Record.txt", "a");
if (ATND == NULL) {
printf("ERROR!");
exit(1);
}
char buf[100];
// avoid magic numbers
// for (i = 0; i < 100; i++) {
for (i = 0; i < STUDENT_N; i++) {
printf("Enter student\'s first name: ");
// don't use scanf()
// scanf("%s", student.fname[i]);
if (fgets(buf, sizeof buf, stdin) == NULL) break;
if (sscanf(buf, "%14s", student[i].fname) != 1) break;
printf("\n\n");
printf("Enter student\'s last name: ");
// Add flush to insure buffered prompts that do not end in \n are sent
fflush(stdout);
// scanf("%s", student.lname[i]);
if (fgets(buf, sizeof buf, stdin) == NULL) break;
if (sscanf(buf, "%14s", student[i].lname) != 1) break;
// /*randomnumber*/student.id[i] = rand() % max + 39048543;
/*randomnumber*/student[i].id = rand() % max + 39048543;
// Do not index the name, index the structure
// fprintf(ATND, "%s %s %d", student.fname[i], student.lname[i], student.id[i]);
fprintf(ATND, "%s %s %d", student[i].fname, student[i].lname, student[i].id);
}
fclose(ATND);
}
Assuming student.fname is a char array of sufficient size
scanf("%s", student.fname[i]);
should be
scanf("%s", student.fname);
You need to pass a pointer to the beginning of the array, NOT the value of the chars, one by one. scanf will enter the entire name in one call.

Linear search for an element within structures saved to file

I have a module which searches through a file to find an index, and if it's found it is supposed to print the details relevant to that index. The program compiles and runs, but no matter if the patient is saved or not, it prints that the patient is not found. What's the logic error I'm missing?
note:patientCount is a global variable which is written to another file and updated every time a patient is added.
void patientSearch(struct patientRec patient[], struct apptRec appt[])
{
system("cls");
int c=0;
char search[6], admit;
printf("Enter the patient ID of the patient you would like to search for.\n");
scanf("%i", &search);
fflush(stdin);
FILE *fp;
fp=fopen("patients.txt", "r");
if(fp==NULL)
{
printf("\nError opening file!");
}
else
{
for (c=0; c<patientCount; c++)
{
if (search==patient[c].patientID)
{
printf("\nPatient found.\n\n");
printf("Patient ID: %i", patient[c].patientID);
fscanf(fp, "%s", patient[c].fName);
printf("\nPatient First Name: ");
puts(patient[c].fName);
fscanf(fp, "%s", patient[c].sName);
printf("\nPatient Surname: ");
puts(patient[c].sName);
fscanf(fp, "%i %c", patient[c].age, patient[c].sex);
printf("\nPatient Age: %i\nPatient Sex: %c", patient[c].age, patient[c].sex);
fscanf(fp, "%s", patient[c].notes);
printf("\n\nNotes: \n");
puts(patient[c].notes);
}
else
{
fscanf(fp, "\n");
}
}
}
fclose(fp);
if (c==patientCount)
{
printf("\nThis patient does not exist. Would you like to admit this patient?\n1: Yes\n2: No\n");
scanf(" %c", &admit);
if (admit=='1')
{
admitPatient(patient, appt);
}
}
}
char search[6], admit;
scanf("%i", &search);
if (search==patient[c].patientID)
Either change to
int search; // This allows the rest of the code to match
or change to
char search[6], admit; //Change the rest of the code to match
scanf("%s", &search);
if (strcmp(search, patient[c].patientID) == 0)
printf("Patient ID: %s", patient[c].patientID);
To make your input and compare in the same format.
Make sure that your search array is big enough to include the final '\0'

Resources