Iostream support for file positioning operations depends on the character encoding of a particular stream. For fixed-width encodings, such as ASCII or UNICODE, the file stream classes allow a full set of positioning operations comparable to those offered by `C' stdio. The options for variable-width and state-dependent encodings, such as JIS, are limited. For these more complex encodings, the only allowed positioning operations are 'seek to beginning', 'seek to end', or 'seek to a previously known location'. The last option requires that you arrive at and then store a particular position before a seek can be performed. Attempting to seek to an arbitrary offset on a stream with variable-width or state-dependent character encodings has an undefined effect on the file position. Here's an example of the correct way to seek to a position in any file stream, regardless of encoding:
int main(int argc, char argv[]) { std::ofstream fs("foo.out"); fs << "Anyone"; //1 std::ofstream::pos_type p = fs.tellp(); //2 fs << " remember J.P. Patches?"; //3 fs.seekp(p); //4 }
//1 | Here we output some characters in order to move the file position to some arbitrary location that we'll later seek back to. |
//2 | Now we use the tellp() function to obtain the current file position. |
//3 | Output some more text to move the file position along. |
//4 | Finally, seek back to our previously saved position. |
This is the only possible method for seeking to an arbitrary position--that is, a position other than beginning or end of file--in a stream with variable or state-dependent character encoding. Of course, the method also works with fixed-width encodings.
The example above shows one use of two of the output file positioning functions, tellp() and a version of seekp(). An ofstream also has another version of the seekp() function that allows a seek to an arbitrary offset in much the same way that the `C' stdio fseek() function works. This function can be used to seek to the beginning or end of any ofstream, or anywhere else in an ofstream with a fixed-width character encoding. For instance:
std::ofstream fs("foo.out"); fs << "Anyone remember J.P. Patches?"; fs.seekp(-2, std::ios_base::cur); //1 fs.seekp(0, std::ios_base::beg); //2
//1 | Seek back two characters. Position at the s in Patches. |
//2 | Seek to beginning of the file. |
The first parameter of this function is a value of std::ofstream::off_type, and the second is one of three constants indicating starting position for the seek. These three values correspond to the three possible seek types available with the `C' stdio function fseek(). They are defined in the base class std::ios_base. The table below summarizes the three different kinds of seeks possible with this version of seekp():
Type of seek | Argument to seekp | `C' stdio equivalent |
seek relative to beginning of file | ios_base::beg | SEEK_SET |
seek relative to end of file | ios_base::end | SEEK_END |
seek relative to current position | ios_base::cur | SEEK_CUR |
As in the example, passing 0 as the offset with std::ios_base::beg as the seek type seeks to the beginning of the file. Likewise, using 0 with std::ios_base::end seeks to the end of the file. Since the function returns the current position after the seek operation, passing 0 along with std::ios_base::cur gets you the current file position without moving it. This is equivalent to calling the tellp() member function.
ifstream provides the same set of functions but with slightly different names: tellg() instead of tellp(), and seekg(...) instead of seekp(...). The reason for this specialized naming scheme can be seen in the fstream class, which provides both sets of functions so that the input and output streams can be manipulated separately.
If you look at the iostream class definitions, you notice that the seek functions are not defined in these classes. Instead, they are inherited from a base class template: basic_ostream for tellp() and seekp(), and basic_istream for tellg() and seekg(). These functions then call (indirectly, through their public interface) virtual functions in the stream buffer, where seeking is actually implemented. Seek functions for ofstream, ifstream, and fstream actually call pubseekoff() and pubseekpos() in filebuf. The code looks like this:
template<class CharT, class Traits> basic_ostream<CharT, Traits>& basic_ostream<CharT, Traits>::seekp (pos_type pos) { if (!this->fail () && -1 == this->rdbuf()->pubseekpos (pos, ios_base::out)) this->setstate (ios_base::failbit); return *this; }
Calling virtual functions in the stream buffer maintains the fundamental separation of buffer manipulation and I/O from string formatting. While it's not necessary to know this in order to use file seek operations, it is important if you ever need to subclass a stream buffer.