I am trying to think of logic on how to read the last line of a file but I cannot come up with answers. ACCOUNTNUM info is a structure. my .dat file who has 3 acc numbers already. Here I want to know how to get the last account number/line which is 2022-3.
This is the function
LastAccountNumber(){
ACCOUNTNUM info;
file = fopen("acctNumbers.dat","r");
char firstAcc[50]="2022-1";//ignore this I was gonna compare this with the last line.
while((fread(&info,sizeof(struct accountNum),1,file))==1){
printf("\nAcc No %s",info.sAccountNum);
}
}
}
This is my structure
struct accountNum{
int sequence;
char sAccountNum[16];
};
typedef struct accountNum ACCOUNTNUM;
Content of my acctNumbers.dat file.
2022-1 ú· 2022-2 ú· 2022-3 ú·
CodePudding user response:
You could call fread
in a loop until it returns 0
, and then print the last record read:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct accountNum
{
int sequence;
char sAccountNum[16];
} ACCOUNTNUM;
int main( void )
{
FILE *fp;
ACCOUNTNUM info;
bool success = false;
//open file and verify that it is open
fp = fopen("acctNumbers.dat","r");
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//skip to last record
while( ( fread( &info, sizeof info, 1, fp) ) == 1 )
success = true;
//print last record, if it exists
if ( success )
printf( "Acc No: %s\n", info.sAccountNum );
else
printf( "No records found.\n" );
//cleanup
fclose( fp );
}
However, this is only guaranteed to work if you are sure that the last fread
will will not perform a partial read, i.e. if you are sure that the file size is an exact multiple of sizeof(ACCOUNTNUM)
. Because if fread
does perform a partial read, then the buffer content will be indeterminate.
If you cannot exclude a partial read, then it would probably be best to use two buffers for reading, and to use them alternately:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct accountNum
{
int sequence;
char sAccountNum[16];
} ACCOUNTNUM;
int main( void )
{
FILE *fp;
ACCOUNTNUM info[2];
int current_index = 0;
bool success = false;
size_t ret;
//open file and verify that it is open
fp = fopen("acctNumbers.dat","r");
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
for (;;) //infinite loop, equivalent to while(1)
{
//read record from file
ret = fread( &info[current_index], sizeof *info, 1, fp);
//swap buffer index
current_index = current_index == 0 ? 1 : 0;
//restart loop if successful
if ( ret == 1 )
{
success = true;
continue;
}
//break out of loop
break;
}
//print last record, if it exists
if ( success )
printf( "Acc No: %s\n", info[current_index].sAccountNum );
else
printf( "No records found.\n" );
//cleanup
fclose( fp );
}
CodePudding user response:
You are reading records one by one and storing it in info
, it stands to reason that the element ACCOUNTNUM
stored in info
when the cycle ends is exactly the last record in the file, take the following code which is a slight modification of yours, to illustrate the point:
#include <stdio.h>
struct accountNum {
int sequence;
char sAccountNum[16];
};
typedef struct accountNum ACCOUNTNUM;
int LastAccountNumber(ACCOUNTNUM* info) {
int success = 0;
FILE* file = fopen("acctNumbers.dat", "rb");
if (file == NULL) {
return -1; // Allows you to document different types of errors
}
while ((fread(info, sizeof(struct accountNum), 1, file)) == 1) {
success = 1;
}
fclose(file);
return success;
}
int main() {
ACCOUNTNUM N[] = {{123, "2022-1"}, {111, "2022-2"}, {321, "2022-3"}};
FILE* file = fopen("acctNumbers.dat", "wb");
fwrite(N, 1, sizeof N, file);
fclose(file);
ACCOUNTNUM info;
if (LastAccountNumber(&info) == 1) //if -1 couldn't open file, 0 no record read
printf("\nAcc No %s", info.sAccountNum);
}
Will output:
Acc No 2022-3
Which is exactly the last element in the file.
Live sample: https://onlinegdb.com/q760Ow1vQc
Note that you should verify the return value of fopen
to confirm the correct access to the file. Closing the file after you are done with it is also advised.