Meathead
Meathead

Reputation: 523

Are fgetpos() and fsetpos() only for text mode? What location/offset data is the fpos_t object filled with if not number of bytes?

I understand the workings of ftell() and fseek() in C, but for this question I couldn't find any precise answer anywhere, including from the closest post on StackOverflow(LINK).

So can you please answer the following:

The function fills the fpos_t object pointed by pos with the information needed from the stream's position indicator to restore the stream to its current position

Upvotes: 3

Views: 842

Answers (3)

zwol
zwol

Reputation: 140826

Besides the reasons mentioned in the other answers, it might be necessary to use fgetpos and fsetpos if you are working with very large files, files that contain more than LONG_MAX bytes. This is a real concern on systems where LONG_MAX is 231 − 1; files with more than two billion bytes in them are not that uncommon nowadays.

If you are on a system that implements POSIX.1-2001, there is a better alternative, which is to #define _FILE_OFFSET_BITS 64 before including any system header files, and then use fseeko and ftello. These are just like fseek and ftell except that they take/return an off_t quantity, which, provided you have made the above #define, is guaranteed to be an integer type that can represent 263 − 1, which ought to be enough for anybody. This is better because you can do arithmetic on off_t; you can't use fpos_t to go somewhere you haven't already been. But if you're not on a POSIX system, fgetpos and fsetpos might be your only option.

(Note that some systems will give you an fpos_t that can't represent a file offset greater than LONG_MAX bytes. On some of those, applying the same #define _FILE_OFFSET_BITS 64 setting will help. On others, you're just completely out of luck if you want a huge file.)

Upvotes: 2

Steve Jessop
Steve Jessop

Reputation: 279385

Not quite. Clues can be found from Beej:

On virtually every system (and certainly every system that I know of), people don't use these functions, using ftell() and fseek() instead. These functions exist just in case your system can't remember file positions as a simple byte offset.

And Linux man pages:

On some non-UNIX systems, an fpos_t object may be a complex object and these routines may be the only way to portably reposition a text stream.

And on Windows:

It assumes that any \n character in the buffer was originally a \r\n sequence that had been normalized when the data was read into the buffer.

That is to say, files that aren't (Windows-linebreak) text files go wrong in Windows when opened in text mode because fsetpos is assuming the file really was a (Windows-linebreak) text file and therefore cannot contain a \n with no \r.

The C11 standard says (my emphasis):

7.21.2/6:

Each wide-oriented stream has an associated mbstate_t object that stores the current parse state of the stream. A successful call to fgetpos stores a representation of the value of this mbstate_t object as part of the value of the fpos_t object. A later successful call to fsetpos using the same stored fpos_t value restores the value of the associated mbstate_t object as well as the position within the controlled stream.

Note that fseek and ftell have nothing to say about the mbstate_t object: they do not report or restore it. So on wide-oriented streams (that is to say, streams on which you've used wide-oriented I/O functions) they only reset the file position, not (if the implementation actually has more than one possible value of a mbstate_t object) the whole state of the stream.

Wide-oriented streams aren't the same thing as text streams, it's just that reading wide text files is the common use for them. Actually fseek and ftell are documented to be able to reset the file position on text files, provided you use them correctly. So I believe (I might be wrong) that fsetpos and fgetpos are only required when using wide I/O functions on the stream.

Upvotes: 2

Christophe
Christophe

Reputation: 73542

fgetpos() and fsetpos() are relevant for both text and binary mode.

The advantage of fgetpos() is that is keeps the full position in the stream, including its internal state, so that you can restore is later. This works whether you are in text mode or not. This is especially important if you are using wide oriented streams or mix fgetc() and fgetwc() in the same file, because some locale use a state dependent multibyte encoding (state depends on previous reads).

fseek() and ftell() can also work with text and binary mode. However there is an important restriction in text mode: you should only use fseek() with 0 or a value previously returned by ftell() (in binary mode you can use whatever value you want). This is because the text mode reading can change the number of bytes returned from reading compared to the bytes effectively in the file (typical example, the 2 CR+LF bytes in a windows file which are converted to a signe LF byte).

As ftell() only returns a long int offset, it can't keep track of the multibyte state if this would be needed. So using fseek() might loose this state.

Upvotes: 4

Related Questions