=============================================================
edit : im sorry it seems like i havent explained well
what my program does is
open file for reading => load list from the file => operations chosen by user will be applies to list (not file)(add-search-display-reset)\ => import change to file(in case there is any)
if the user chooses to only add an employee and quit will it be better to append the added node to the end of file, close and free list or its okay to open file for reading and overwrite all the nodes from the beggining
this last option will save me a lot of lines of code but will it save time and energy for the user while execution ?
=============================================================
i am workig on a (c language) school project where we have to use both lists and files to :
display a list of employees
add employees
search an employee
reset list
save and quit
so i am looking for the best way to do it, and as i know a good code is the one that ensures the program uses as less memory as it can and be as fast as it can.
what i am asking you is
after opening the file that contains the infos of employees and loadig it to the list, and modifying the list by add or reset, would it be better to :
open the file for "w" and fill it with the list.
or open it for "a+" to add from last employee.
note that this last way i will have to memorize the initial_number_of_lines (aka nodes) and look for the 'initial last node' to start loading from it.
note : the employee variables are first_name last_name and salary; VERIFY(file) returns 1 i file opened returns 0 if not;
void load_file(list *list, char *filename)
{
if (current_number_of_lines != initial_number_of_lines || list_reset == 1)
{
if (current_number_of_lines > initial_number_of_lines && list_reset == 0)
{
FILE *file = fopen(filename, "a+");
if (VERIFY(file) == 1)
{
for (int i = 0; i < initial_number_of_lines; i++)
{
list = list->next;
}
while (list != NULL)
{
fprintf(file, "%s\t%s\t%lf\n", list->emp.Fname, list->emp.Lname, list->emp.salary);
list = list->next;
}
}
fclose(file);
}
else if (list_reset == 1)
{
FILE *file = fopen(filename, "w");
while (list != NULL)
{
fprintf(file, "%s\t%s\t%lf\n", list->emp.Fname, list->emp.Lname, list->emp.salary);
list = list->next;
}
fclose(file);
}
else
printf("Error\n");
}
freeList(list);
}
If all your add operations adds the new node to the end of the list, you will get better performance using a+. On the other hand, the code will be more simple if you always rewrite the the whole file. So what is most important for you? Performance or simple code? I would start with the simple approach.
Some other notes.
It's pretty strange that you call the function load_file when it actually writes the file.
The variables: current_number_of_lines, initial_number_of_lines, list_reset seem to be global variables. Using global variables is something you should avoid.
The logic (aka if statements) seems too complicated. You can simply do:
void load_file(list *list, char *filename)
{
if (list_reset == 1)
{
...
}
else if (current_number_of_lines > initial_number_of_lines)
{
...
}
else if (current_number_of_lines < initial_number_of_lines)
{
printf("Error\n");
}
freeList(list);
}
If you use "w" to fopen() then it will truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file. You have to write all records to the file.
If you use "a+" to fopen() then the file will be open for reading and appending (writing at end of file). The file is created if it does not exist. Output is always appended to the end of the file. Warning: POSIX is silent on what the initial read position is when using this mode. In the mode, you will only write records that needs to be added.
Please also note that neither of the methods are well suited for sharing the data between two or more applications.
If new records are added at the end of the list, obviously it is faster to append those records ate the end of the file ("a+" mode).
Since you have a text file (made of text lines), it is unfortunately not possible to update the changed records. In that case, you must write everything from the list to the file ("w" mode).
If you change you file format in order to use a fixed size record then you can optimize the writing by positioning the file on the record and then write. For that, you'll define your record as a struct containing fixed size strings. And of course add an item in that struct to remember where it has been read from the list.
Edit: If it matters, I'm using Dev C++ with -std=c99 as an option.
My professor was able to run this code in class and successfully open a file that eventually reads the data into a linked list. When running the exact same code, my program abruptly exits despite the file being successfully opened.
All I'm trying to do is get this code to run. I've solved what he wants us to solve in my own example, but I can't figure out why his code doesn't run on my machine.
I did add in a puts("Success") line to verify the file has been opened, and I've gone through the methods it calls to see if I can find an error, but I cannot.
Here is the method with the issue (I'm assuming)
int ReadFileStoreInList(void)
{
FILE *cfPtr;
if ((cfPtr = fopen("Hertz-Homework-9-List.txt", "r")) !=NULL)
{
char make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
char rented = 'A';
float daily_rate=0.0;
char dAilyRate[TWELVE];
fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);
while (!feof(cfPtr))
{
fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);
rented = 'A';
add_at_end(make, model, size, color, power, daily_rate, rented);
}
printScreenTitleAndHeaderForCars();
traverse_in_order();
}
else
{
puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
}
fclose(cfPtr);
}
This calls traverse_in_order(); and printScreenTitleAndHeaderForCars();, which I will list below.
void traverse_in_order()
{
node *ptr;
if(start==NULL)
{
printf("list is empty\n");
return;
}
printScreenTitleAndHeaderForCars();
for(ptr=start; ptr!=NULL; ptr=(*ptr).next)
printf("%-12s%-12s%-12s%-12s%-12s%9.2f%12c\n", ptr->make, ptr->model, ptr->size, ptr->color, ptr->power, ptr->daily_rate, ptr->rented);
}
void printScreenTitleAndHeaderForCars()
{
system("Cls");
printf("%35s\n\n","Hertz Rental Cars");
printf("%79s\n","Avail");
printf("%-12s%-12s%-12s%-12s%-12s%-12s%8s\n", "Make", "Model", "Size", "Color", "Power", "Daily_Rate", "Rented");
for (int x=0; x< 7; x++)
printf("----------- ");
printf("\n");
}
In case the structure/header file was also needed:
#define TWELVE 12
typedef struct node_type
{
char make[TWELVE];
char model[TWELVE];
char size[TWELVE];
char color[TWELVE];
char power[TWELVE];
float daily_rate;
char rented;
struct node_type *next;
} node;
node *start=NULL;
int ReadFileStoreInList(void);
void add_at_beginning();
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented);
void add_after_element();
void add_before_element();
void traverse_in_order();
void traverse_in_reverse_order(node *);
void delete_at_beginning();
void delete_at_end();
void delete_after_element();
void delete_before_element();
void sort();
void doSomething(); // menu to ask operator what to do
void RentaCar();
void FindCarAndUpdateAsRented(char *modelSelected);
void ReturnCar();
void toTitleCase(char *aString);
void printScreenTitleAndHeaderForCars();
Requested add_at_end function:
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented)
{
node *ptr, *loc;
ptr = (node *) malloc(sizeof(node));
if(ptr==NULL)
{
printf("no space\n");
return;
}
strcpy((*ptr).make,make);
strcpy((*ptr).model,model);
strcpy((*ptr).size,size);
strcpy((*ptr).color,color);
strcpy((*ptr).power,power);
(*ptr).daily_rate = daily_rate;
(*ptr).rented = rented;
if(start==NULL)
{
start=ptr;
(*start).next=NULL;
}
else
{
loc = start;
while((*loc).next != NULL)
loc=(*loc).next;
(*loc).next=ptr;
(*ptr).next=NULL;
}
}
Interesting enough, if I rename this file to something that isn't correct, the proper printScreenTitleAndHeaderForCars() text executes and shows on screen. That's why I believe it has something to do with ReadFileStoreInList();
I've tried to debug this for a few hours now, but with the knowledge I've learned in class I just can not figure out why this doesn't run.
I expect the output to have the header information given from printScreenTitleAndHeaderForCars(), and then the data from the file I'm reading to appear on screen.
When the file is named improperly in my code, it runs this:
Hertz Rental Cars
Avail
Make Model Size Color Power Daily_Rate Rented
----------- ----------- ----------- ----------- ----------- ----------- -----------
list is empty
enter choice
1. Select Model and Rent
2. Select Model and Return
5. traverse in order
11. sort
12. exit
Where "list is empty" is situated, it should be populating the data from the text file and putting it there.
Instead, I just get:
--------------------------------
Process exited after 1.433 seconds with return value 3221225477
Press any key to continue . . .
I have a feeling this has to do with the way the pointers are written, but I'm struggling to understand how he could run the code and I couldn't.
Any knowledge as to why this might happen would be appreciated!
Edit: Text file contents:
make, model, size, color, power,daily rate.
Mazda,3,4-door,Black,4-Cyl,$99.73
Jeep,Cherokee,4-door,Blue,8-Cyl,$131.92
Buick,Regal,4-door,Purple,6-Cyl,$125.19
Fullsize,SUV,5-door,Brown,8-Cyl,$163.94
Chrysler,Pacifica,4-door,Green,6-Cyl,$127.49
Ford,Focus,2-door,Red,4-Cyl,$99.73
VW,Jetta,2-door,Orange,4-Cyl,$94.91
Chevrolet,Suburban,4-door,Yellow,8-Cyl,$204.92
Nissan,Pathfinder,4-door,White,6-Cyl,$145.11
Chevrolet,Spark,2-door,Teal,4-Cyl,$99.55
You concluded in comments that you thought the data file contents were to blame for your issue. You now having posted those, I can confirm that they are indeed a contributing factor. Imprudent details of your scanf format also contribute.
In the first place, the call for reading the header is dangerous:
fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);
Since the data are just going to be discarded, there is no point in storing them. Moreover, it's not necessarily safe to assume that the header data will have characteristics matched to those of the associated data. It would be better to read and not assign the whole line:
fscanf(cfPtr, "%*[^\n]");
The * in the format says that the field directive is not to be assigned, only read. Overall, that reads (and ignores) everything up to but not including the first newline. That also allows you to get rid of the abominably-named dAilyRate variable.
Then there is the format for reading the actual data:
fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);
It simply does not match the data. Specifically, the %s field descriptor skips leading whitespace and matches a whitespace-delimited string. Your data are comma-delimited, not whitespace-delimited except for line terminators. As a result, that scanf call will try to write a whole line's worth of data into each of the first five strings, thus overrunning each of their bounds. That's a plausible reason for a segfault.
What's more, the line read into daily_rate will fail, since the next data available at that point will be non-numeric. Yet even if the commas were changed to spaces, the rate data would still not be read correctly, because $ is not a valid part of a number. And that, in turn will throw off the reads for the second and subsequent lines.
The field overruns could have been avoided by specifying maximum field widths in the format. It would, moreover, be prudent to check the return value of scanf() to verify that all fields were read, as #Achal demonstrated in his answer, before relying on that data.
Here's a data file in a format compatible with the formats you're actually using:
make model size color power rate
Mazda 3 4-door Black 4-Cyl 99.73
Jeep Cherokee 4-door Blue 8-Cyl 131.92
Buick Regal 4-door Purple 6-Cyl 125.19
Fullsize SUV 5-door Brown 8-Cyl 163.94
Chrysler Pacifica 4-door Green 6-Cyl 127.49
Ford Focus 2-door Red 4-Cyl 99.73
VW Jetta 2-door Orange 4-Cyl 94.91
Chevrolet Suburban 4-door Yellow 8-Cyl 204.92
Nissan Pathfinder 4-door White 6-Cyl 145.11
Chevrolet Spark 2-door Teal 4-Cyl 99.55
And here's a safer way to read it:
#define STR_SIZE 12
char make[STR_SIZE]={""}, model[STR_SIZE]={""}, size[STR_SIZE]={""}, color[STR_SIZE]={""},
power[STR_SIZE]={""};
float daily_rate;
fscanf(cfPtr, "%*[^\n]");
while (fscanf(cfPtr, "%11s%11s%11s%11s%11s%f", make, model, size, color, power, &daily_rate) == 6) {
add_at_end(make, model, size, color, power, daily_rate, 'A');
}
The %11s fields will read up to 11 characters into your 12-character arrays, leaving room for the string terminator that fscanf() will append to each. This will still run into trouble if there is overlong data in the file, but it should not segfault.
I should say also that scanf is difficult to use safely and properly, and that there are other, better alternatives for parsing and consuming the data -- either in the original or in the modified format.
Here are few observation.
Firstly provide more readbility to your code by giving some meaningful macro name instead of TWELVE. For e.g #define NUMBER_OF_ITEMS 12
Secondly, In function definition the formal argument make[TWELVE] doesn't look good as you are passing char array & it decays to char*, so only char *make is enough.
This
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented) { }
can be replaced as
void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented) { }
And most importantly, this
fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate); /* Just Remove it */
just before
while (!feof(cfPtr))
creates an issue i.e that information you are not using, its getting overwritten by second fscanf() statement inside loop. Also do read Why is “while (!feof(file))” always wrong?
Sample code
void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented)
{
/* same code */
}
And
int ReadFileStoreInList(void)
{
FILE *cfPtr;
if ((cfPtr = fopen("input", "r")) !=NULL)
{
char make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
char rented = 'A';
float daily_rate=0.0;
char dAilyRate[TWELVE];
while (fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate) == 6)
{
rented = 'A';
add_at_end(make, model, size, color, power, daily_rate, rented);
}
printScreenTitleAndHeaderForCars();
traverse_in_order();
}
else
{
puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
return 0; /* in case of fopen failed */
}
fclose(cfPtr);
}
I wrote a small program in C that creates a list of students in a binary file. I call function fsearch() (below) to search for a specified student and change his data, but the data seems not to be modified.
// the file is opened in mode "wb+"
int fsearch(FILE *f)
{
student s;
float matsearch;
printf("enter the matricule you want to find ");
scanf("%f",&matsearch);
rewind(f); // starting the search from the beginning
while(fread(&s,sizeof(student),1,f)==1 && s.mat!=matsearch);
if(s.mat==matsearch)
{
printf("we found what searched for\n");
printf("name: %s\n",s.fname);
printf("last name: %s\n",s.lname);
printf("matricule: %.f\n",s.mat);
fseek(f,-sizeof(student),SEEK_CUR);
student a;
scanf("%s",&(a.fname));
scanf("%s",&(a.lname));
scanf("%d",&(a.mat));
if(fwrite(&a,sizeof(student),1,f)==1)
{
printf("successfully wrote"); // this message does get printed
}
return(1); // successfully found
}
printf("we didn't find what you searched for\n");
return(0);
}
In addition to the one posted by bluesawdust, I found some other mistakes in the code:
// the file is opened in mode "wb+": this means that your file was destroyed on open (see here). You might want to use "rb+"
since you didn't initialize your student s structure (and no record was ever written in it because of my previous point) s.mat contains a random value
scanf("%d",&(a.mat));: as for printf, you should change the format string to "%f" (but actually you should use a string type, comparing floats with == is not good practice because of the roundings)
sizeof(student) is unsigned, so negating it is not appropriate here. You should cast it to an int before negating.
English is not my motherlanguage, I'm sorry for any grammar mistakes in the description or in the code, I translated it in order to share it here with you.
Hello, I'm writing a little program in C and I need some help, I'm stuck with one error I can't fix, I searched here on forums and anywhere else, but nothing I found helped me. The other functions in the program work just fine.
This function reads a list of words and categories from txt file, puts it into a structure, makes a list. The user types what word he wants to delete from file, so function searches if it's there and deletes if it is.
I'm not the best with lists so there's probably a really basic, stupid problem here, any help please?
void REMOVE_WORD (int howmanylines)
{
FILE *fp;
if ((fp=fopen("words.txt", "r+w"))==NULL)
{printf("Error while opening the file!\n");
exit(1);}
else
{
typedef struct base
{
struct base *next;
char word[25];
char category[15];
} list_els;
struct base tab[howmanylines];
int i=0;
while (!feof(fp))
{
fscanf(fp, "%s", &tab[i].word);
fscanf(fp, "%s", &tab[i].category);
i++;
}
fclose(fp);
list_els *head;
list_els *el=(list_els*)malloc(sizeof(list_els));
list_els *ind=head;
while (ind->next)
{
ind=ind->next;
ind->next=el;
}
printf("What word do you want to remove?\n\n");
char word_remove[25];
scanf("%s", &word_remove);
if (strcmp(ind->next->word, word_remove)==0)
{
printf("Found:\n Word: | Category:\n %s | %s\n", ind->next->word, ind->next->category);
printf("Are you sure you want to remove?\n1)Yes\n 2)No\n\n");
int removing;
if (removing==1)
{
list_els *removed=ind->next;
ind->next=removed->next;
free(removed);
}
else if (removing==2) {printf("Removing cancelled.\n\n");}
else {printf("Wrong operation number!");}
}
else printf("Word not available in the base!\n\n");
}
}
It shows me an error 'struct base' has no member named 'word' in the line where I use the strcmp.
In this snippet
list_els *head;
list_els *el=(list_els*)malloc(sizeof(list_els));
list_els *ind=head;
while (ind->next)
{
ind=ind->next;
ind->next=el;
}
You are not initialising ind to any valid value. You probably meant to write ind = el instead of head. Also, don't cast malloc().