Home > Software design >  Reading last line of a file In C
Reading last line of a file In C

Time:05-03

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.

  • Related