clownzxy
clownzxy

Reputation: 19

Reading last line of a file In C

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       ú·

Upvotes: 0

Views: 180

Answers (2)

Andreas Wenzel
Andreas Wenzel

Reputation: 25396

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 )
    {
        //set variable to indicate that we have found at
        //least one record
        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 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 the possibility of a partial read, then you could use two buffers for reading, and 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 )
        {
            //set variable to indicate that we have found at
            //least one record
            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 );
}

Or you could use a single buffer and change the way you are calling the function fread, by swapping the second and third function arguments, so that you can detect whether a partial read occurred. If it does occur, you can print an error message and terminate the program.

#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;
    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 );
    }

    //read one record per loop iteration
    while ( ( ret = fread( &info, 1, sizeof info, fp) ) != 0 )
    {
        //verify that no partial read occurred
        if ( ret != sizeof info )
        {
            fprintf( stderr, "Error: Partial read detected!\n" );
            exit( EXIT_FAILURE );
        }

        //set variable to indicate that we have found at
        //least one record
        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 );
}

Upvotes: 2

anastaciu
anastaciu

Reputation: 23832

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.

Upvotes: 1

Related Questions