Reputation: 310
I'm trying to write a method that will, given a file containing values to update or append, will update a second binary file.
Apparently, when I overwrite a struct in the binary file, the offsets somehow change and that corrupts everything after it. Am I doing something wrong, and is there a way to prevent this without truncating and appending to the file?
Current code:
typedef struct{
int number;
double price;
} stock;
void update(char* updatefile, char* binfile){
FILE *fin, *fout;
stock *currStock;
stock *updateStock;
int currPos;
int update;
int val1=0; double val2=0;
currStock = malloc(sizeof(stock));
updateStock = malloc(sizeof(stock));
fin=fopen(updatefile,"r");
while (fscanf(fin, " \n%d %lf",&val1,&val2) != EOF) {
currStock->number = val1;
currStock->price = val2;
printf("Updating file with stock: %d,%1.2lf\n",currStock->number,currStock->price);
fout = fopen(binfile,"r+b");
update = 0;
while(fread((void*)updateStock,sizeof(stock),1,fout)==1&&!update){
printf("position: %ld\n",ftell(fout));
printf("update stock: %d, %1.2lf\n",updateStock->number,updateStock->price);
if(updateStock->number==currStock->number){ //&&updateStock->price!=currStock->price
printf("updating stock with new price: %1.2lf\n",currStock->price);
currPos = ftell(fout);
printf("ftell = %d\n",currPos);
fseek(fout,currPos-sizeof(stock),SEEK_SET);
printf("ftell after seek: %ld\n",ftell(fout));
fwrite(currStock,sizeof(stock),1,fout);
//fseek(fout,sizeof(stock),SEEK_CUR);
update = 1;
}
}
if(!update){
fseek(fout,0,SEEK_END);
fwrite(currStock,sizeof(stock),1,fout);
}
if(fclose(fout)){
printf("value updated\n");
}
}
if(!feof(fin)){
printf("Error reading from file. Please check file format\n");
exit(0);
}
if(fclose(fin)){
puts("Error closing update file");
}
printf("File updated.\n");
free(currStock);
free(updateStock);
return;
}
output: (using another method to display binary file contents)
stock in file: 1, 2.50
stock in file: 2, 5.43
stock in file: 3, 12.32
stock in file: 4, 0.54
stock in file: 5, 7.23
Updating file with stock: 2,3.40
position: 16
update stock: 1, 2.50
position: 32
update stock: 2, 5.43
updating stock with new price: 3.40
ftell = 32
ftell after seek: 16
Updating file with stock: 4,6.50
position: 16
update stock: 1, 2.50
position: 32
update stock: 2, 3.40
position: 48
update stock: 2, 5.43
position: 64
update stock: 1088, -41614952599525078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.00
position: 80
update stock: 1343, 0.00
Updating file with stock: 7,6.12
position: 18
update stock: 1, 2.50
position: 34
update stock: 2, 3.40
position: 50
update stock: 2, 5.43
position: 66
update stock: 1088, -41614952599525078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.00
position: 82
update stock: 1343, 0.00
File updated.
stock in file: 1, 2.50
stock in file: 2, 3.40
stock in file: 2, 5.43
stock in file: 1088, -41614952599525078000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.00
stock in file: 1343, 0.00
edit: I know this isn't the most efficient way to update the file (opening an closing for each update), but I want to figure out why it's corrupting it before I fix the algorithm.
edit2: Got it to work using truncating and appending, but I'd still like to know why this doesn't work.
Upvotes: 2
Views: 5611
Reputation: 1977
It looks like the problem is occurring because you are performing a read directly following a write with a file that has been opened with the mode r+. The book "C In a Nutshell" states:
If the mode string includes a plus sign, then the mode allows both input and output, and you must synchronize the file position indicator between reading from and writing to the file. Do this by calling fflush() or a file positioning function -- fseek(), fsetpos(), or rewind() -- after writing and before reading, and by calling a file-positioning function after reading and before writing (unless it is certain that you have read to the end of the file).
The issue is in your nested-most while() loop here:
/*** Read occurs here... ***/
while(fread((void*)updateStock,sizeof(stock),1,fout)==1&&!update){
printf("position: %ld\n",ftell(fout));
printf("update stock: %d, %1.2lf\n",updateStock->number,updateStock->price);
if(updateStock->number==currStock->number){
printf("updating stock with new price: %1.2lf\n",currStock->price);
currPos = ftell(fout);
printf("ftell = %d\n",currPos);
fseek(fout,currPos-sizeof(stock),SEEK_SET);
printf("ftell after seek: %ld\n",ftell(fout));
/** Write occurs here but...
during the next while() check a read is immediately performed. **/
fwrite(currStock,sizeof(stock),1,fout);
update = 1;
}
I added the following immediately after your fwrite() and it seems to be working...
fflush(fout);
Also, just a side note. Your currPos is an int and ftell() returns a long (there is not guarantee that your int will hold a long value).
Hope that helps!
Upvotes: 3