Reputation: 339
I want to do the following in C.
I have a random access data file containing some records.
The records are in the following format:
Acct# First Name Last Name Balance
0 "" "" 0.0
0 "" "" 0.0
0 "" "" 0.0
05 Joe Costanza 0.50
0 "" "" 0.0
0 "" "" 0.0
0 "" "" 0.0
19 Jason Bourne 58.00
0 "" "" 0.0
0 "" "" 0.0
42 Andy Der -15.12
0 "" "" 0.0
0 "" "" 0.0
I want to subtract an amount from balance from all the records that have a non zero account number and write the new updated balance to the file for those records.
This is what I have tried so far to accomplish the above.
#include <stdio.h>
struct clientData
{
int acctNum;
char lastName[15];
char firstName[10];
double balance;
};
void textFile(FILE *readPtr);
int main(void)
{
FILE *cfPtr;
struct clientData client = {0, "", "", 0.0};
double serviceCharge = 5.0;
cfPtr = fopen("credit.dat", "rb+");
fread(&client, sizeof(struct clientData), 1, cfPtr);
client.balance -= serviceCharge;
fseek(cfPtr,(client.acctNum - 1) * sizeof(struct clientData)
, SEEK_CUR);
fwrite(&client, sizeof(struct clientData), 1, cfPtr);
fclose(cfPtr);
return 0;
}
No matter what I try, I just can't write a single updated record back to the file. I have even tried a single record without any while loop or if statement and it still does not work. Can anybody tell me what I am missing.
loop:
fread( &client, sizeof( struct clientData ), 1, cfPtr);
while (!feof(cfPtr))
{
if ( client.acctNum != 0 )
{
client.balance -= serviceCharge;
fseek(cfPtr, (-1 * sizeof(struct clientData)), SEEK_CUR);
fwrite( &client, sizeof( struct clientData ), 1, cfPtr);
}
fread( &client, sizeof( struct clientData ), 1, cfPtr);
}
Upvotes: 2
Views: 4043
Reputation: 753475
This is the relevant part of the code currently on display:
cfPtr = fopen("credit.dat", "rb+");
fread(&client, sizeof(struct clientData), 1, cfPtr);
client.balance -= serviceCharge;
fseek(cfPtr,(client.acctNum - 1) * sizeof(struct clientData), SEEK_CUR);
fwrite(&client, sizeof(struct clientData), 1, cfPtr);
I will assume that error checking on the function return values is 'not needed'; it should be there, but it will complicate things slightly. However, I wouldn't trust myself to write a program without the error checking - too many things can go wrong for my tastes (and bitter experience).
At another time, we can discuss the wisdom of the binary data format you are using; it is sufficient for the time being.
Here's code that 'works':
#include <stdio.h>
struct clientData
{
int acctNum;
char lastName[15];
char firstName[10];
double balance;
};
static void print_client(struct clientData *data)
{
printf("%04d %-15s %-15s %6.2f\n", data->acctNum, data->lastName,
data->firstName, data->balance);
}
int main(void)
{
FILE *cfPtr;
struct clientData client = {0, "Me", "Mine", 50.0};
double serviceCharge = 5.0;
/* Initialize */
cfPtr = fopen("credit.dat", "wb");
fwrite(&client, sizeof(struct clientData), 1, cfPtr);
fclose(cfPtr);
/* Update */
cfPtr = fopen("credit.dat", "rb+");
fread(&client, sizeof(struct clientData), 1, cfPtr);
print_client(&client);
client.balance -= serviceCharge;
fseek(cfPtr, 0, SEEK_SET);
fwrite(&client, sizeof(struct clientData), 1, cfPtr);
fclose(cfPtr);
/* Validate */
cfPtr = fopen("credit.dat", "rb");
fread(&client, sizeof(struct clientData), 1, cfPtr);
fclose(cfPtr);
print_client(&client);
return 0;
}
This works too...it doesn't skip 0 client IDs because it doesn't generate any. But you can deal with that.
#include <stdio.h>
struct clientData
{
int acctNum;
char lastName[15];
char firstName[10];
double balance;
};
static void print_client(const struct clientData *data)
{
printf("%04d %-15s %-15s %6.2f\n", data->acctNum, data->lastName,
data->firstName, data->balance);
}
/* Initialize file */
static void init_file(const char *filename)
{
FILE *cfPtr = fopen(filename, "wb");
int i;
for (i = 1; i <= 30; i++)
{
struct clientData client = { i, "", "", i * 50.0};
sprintf(client.lastName, "Me (%d)", i);
sprintf(client.firstName, "Mine (%d)", i);
fwrite(&client, sizeof(struct clientData), 1, cfPtr);
}
fclose(cfPtr);
}
static void print_file(const char *filename)
{
struct clientData client;
FILE *cfPtr = fopen(filename, "rb");
while (fread(&client, sizeof(struct clientData), 1, cfPtr) == 1)
print_client(&client);
fclose(cfPtr);
}
int main(void)
{
FILE *cfPtr;
double serviceCharge = 5.0;
const char *filename = "credit.dat";
struct clientData client;
init_file(filename);
printf("Post-initialization: %s\n", filename);
print_file(filename);
/* Update */
cfPtr = fopen(filename, "rb+");
while (fread(&client, sizeof(struct clientData), 1, cfPtr) == 1)
{
client.balance -= serviceCharge;
fseek(cfPtr, -1 * (long)sizeof(struct clientData), SEEK_CUR);
fwrite(&client, sizeof(struct clientData), 1, cfPtr);
fseek(cfPtr, 0, SEEK_CUR);
}
fclose(cfPtr);
printf("Post-update: %s\n", filename);
print_file(filename);
return 0;
}
See also the comments...
Upvotes: 0
Reputation: 104020
while ( !feof( readPtr ) )
{
if ( client.acctNum != 0 )
{
At this point in the code, you haven't read anything. Nothing. :)
You've opened the file, you've checked to see if you've reached the end of the file, but there's no code yet to read anything from the file, so your if
condition is just checking against the initialized structure at the start.
Looping over input typically works best if the input is read at the top of the loop. (There are exceptions, but this doesn't look like one of them.)
Try this as a starting point for your loop:
while(fread(&client, sizeof(struct clientData), 1, filePtr)) {
if (client.acctNum) {
client.balance -= charge;
fseek(filePtr, - sizeof(struct clientData), SEEK_CUR);
if (1 != fwrite(&client, sizeof(struct clientData), 1, filePtr))
perror("Error writing to file");
}
}
The error handling can probably be improved; maybe the whole edit should be aborted. (Mandatory record locking on the file could have some writes fail and some writes succeed, but that is pretty unlikely.)
Update
Your fseek()
call is basing the new file position based on multiplying the account number by the size of the struct clientData
. This works ONLY IF your records are sorted, no numbers are skipped, and it starts at 0
and works up. (Your example input file is not sorted; you even have a record with a 0
account number in the middle of 'valid' records.) So switch to fseek(..., SEEK_CUR)
instead.
Upvotes: 2
Reputation: 86333
Apart from the logic errors in your code that @sarnold pointed out, you never fflush()
your file stream or fclose()
it. Any updates you make probably remain in the stream buffer and become lost as your program terminates.
Upvotes: 0