For a given file pointer (FILE *
), is it possible to rapidly determine the distance from current position to the end of file.
The time it takes to figure out the actual distance should be not dependent to the distance.
For example the subtraction of fpos_t
, but fpos_t is not integer, it cannot be operated numerically. Is there any alternative way?
When you first open the file, you can use fseek()
to go to the end of the file (but see the note below), then use ftell()
to get the position and save this (as the file's size). Then call rewind()
to go back to the beginning.
Then, the return value from any later call to ftell()
can be subtracted from your saved 'size' to get the offset (distance) from the current position to the file's end:
// Given a FILE* fp that's just been opened:
fseek(fp, 0, SEEK_END);
long int endpos = ftell(fp);
rewind(fp); // Or you can use fseek(fp, 0, SEEK_SET);
//...
// Later in your code:
long int dtoend = endpos - ftell(pf);
But note that implementations are not required to implement SEEK_END
: from the cplusplus.com page on fseek
linked above:
- Library implementations are allowed to not meaningfully support SEEK_END (therefore, code using it has no real standard portability).
Just to clarify (following on from some comments): The code above requires that the endpos
value be saved for the duration of the file's open/read operations. One could avoid this by seeking to the end and then restoring the current position at any point, but that would be far less efficient. For example, one could write a function to get the distance-to-end at any time:
long int dtoend(FILE *fp)
{
long int curpos = ftell(fp); // Get current position
fseek(fp, 0, SEEK_END); // Go to the file's end
long endpos = ftell(fp); // Gets the file's size
fseek(fp, curpos, SEEK_SET); // Restore previous pos
return endpos - curpos; // Return the distance!
}
fseek
and ftell
functions that use the long int
type (which is often 32-bits wide) for file positions; to use similar code for larger files, there are a number of (albeit platform-specific) alternatives...
On Windows platforms, using the MSVC
(or compatible) compilers, there are the _ftelli64
and _fseeki64
functions, which can be used in virtually the same way as their 'standard' counterparts; for example, by making the following changes to the above code:
//...
int64_t curpos = _ftelli64(fp); // Get current position
_fseeki64(fp, 0LL, SEEK_END); // Go to the file's end
//... and similar changes elsewhere
On Linux systems, the 64-bit calls are implemented as ftello
and fseeko
if you make sure to #define _FILE_OFFSET_BITS 64
.
Other platforms/compilers may implement either (or both) of the above, or have some other, very similar 64-bit replacements.
fseek
with SEEK_END
as the origin argument can fail in number of different circumstances; for example, if the file pointer is stdin
, if it refers to a pipe stream, or (on some systems) if the file is opened in text mode†. To handle such cases, one should really check the return value of the fseek
call, which will be non-zero if it failed. So, here is a 64-bit version of the dtoend
function with such error handling implemented (note for compilers other than MSVC
or GNU
, you will need to add the relevant definition macros for the bigseek
and bigtell
functions):
#include <stdio.h>
#include <stdint.h>
#if defined (_MSC_VER) // MSVC/Windows...
#define bigtell _ftelli64
#define bigseek _fseeki64
#elif defined (__GNUC__) // GNU/Linux...
#define _FILE_OFFSET_BITS 64
#define bigtell ftello
#define bigseek fseeko
//
// Feel free to add other compiler/platform implementations
//
#else // Unknown platform/compiler - likely to cause warnings!
#define bigtell ftell
#define bigseek fseek
#endif
int64_t dtoend(FILE* fp)
{
int64_t curpos = bigtell(fp); // Saves the file's current position
if (bigseek(fp, 0LL, SEEK_END) != 0) return -1; // -1 can ONLY be an error condition
int64_t endpos = bigtell(fp); // Retrieve file size (end position)
bigseek(fp, curpos, SEEK_SET); // Restore previously saved position
return endpos - curpos; // Subtract to get distance from end
}
† From the same cplusplus.com page linked above:
For streams open in text mode, offset shall either be zero or a value returned by a previous call to ftell, and origin shall necessarily be SEEK_SET. If the function is called with other values for these arguments, support depends on the particular system and library implementation (non-portable).