'Reading and printing the last number of integers from a binary file to the console [closed]

int read_last_ints(const char *file_name, int num_ints) 
{
    FILE *fp = fopen(file_name, "r");
    if (fp == NULL)
    {
        return 1;
    }
    int integers;
    long int size;
    size_t nread;

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    rewind(fp);
    size = size - num_ints;
    fseek(fp, size, SEEK_SET);
    
    while ((nread = fread(&integers, 1, sizeof(int), fp)) > 0)
    {
        printf("%d\n", integers);
    }

    fclose(fp);
    return 0;
}

I am trying to create a function that reads the last integers from a binary file, however my code doesn't work as intended. When I try to test my code, it produces random numbers that don't make sence. If anyone has any advice, please share it. Thank you.

cio


Solution 1:[1]

You need to position backwards from the end of the file by the size of num_ints integers.

A minimalist change to your code is:

int read_last_ints(const char *file_name, int num_ints) 
{
    FILE *fp = fopen(file_name, "r");
    if (fp == NULL)
    {
        return 1;
    }
    int integers;
    long int size;
    size_t nread;

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    rewind(fp);
    size = size - sizeof(int) * num_ints;  /* MNC — minimum necessary change */
    fseek(fp, size, SEEK_SET);
    
    while ((nread = fread(&integers, 1, sizeof(int), fp)) > 0)
    {
        printf("%d\n", integers);
    }

    fclose(fp);
    return 0;
}

However, you can make use of the SEEK_END option more effectively. You should probably also use "rb" instead of "r" to read a binary file. Using "rb" is crucial on Windows; it is harmless on Unix-like systems (because the C standard mandates support for "rb", even though it is not necessary on Unix). This code also renames the variable integers to the singular integer.

int read_last_ints(const char *file_name, int num_ints) 
{
    FILE *fp = fopen(file_name, "r");
    if (fp == NULL)
    {
        return 1;
    }
    int integer;
    size_t nread;

    fseek(fp, -(long)(num_ints * sizeof(int)), SEEK_END);
    
    while ((nread = fread(&integer, 1, sizeof(int), fp)) > 0)
    {
        printf("%d\n", integer);
    }

    fclose(fp);
    return 0;
}

This saves a call to fseek(), ftell() and rewind(). The rewind() was always superfluous, but you don't need to know the actual size of the file. The fseek() call simply seeks to the relevant number of bytes before the end of the file. You'd only have problems if the file is not big enough to hold that many integers (and it would just do its best, seeking to the start and reading as much as possible).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jonathan Leffler