Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
Each type of iostream has a concept of where its next
character will come from (if it s an istream) or go (if it s an ostream).
In some situations, you might want to move this stream position. You can do so
using two models: one uses an absolute location in the stream called the streampos; the second works like the Standard C library functions fseek( ) for a file and moves a given number of bytes from the beginning, end, or current
position in the file.
The streampos approach requires that you first call a
tell function: tellp( ) for an ostream or tellg( ) for an istream. (The p refers to the put pointer, and the g refers to the get pointer. ) This function returns a streampos you can
later use in calls to seekp( ) for an ostream or seekg( ) for an istream when you want to return to that position in the stream.
The second approach is a relative seek and uses overloaded
versions of seekp( ) and seekg( ). The first argument
is the number of characters to move: it can be positive or negative. The second
argument is the seek direction:
ios::beg
|
From beginning of stream
|
ios::cur
|
Current position in stream
|
ios::end
|
From end of stream
|
Here s an example that shows the movement through a file,
but remember, you re not limited to seeking within files as you are with C s stdio.
With C++, you can seek in any type of iostream (although the standard stream
objects, such as cin and cout, explicitly disallow it):
//: C04:Seeking.cpp
// Seeking in iostreams.
#include <cassert>
#include <cstddef>
#include <cstring>
#include <fstream>
#include "../require.h"
using namespace std;
int main() {
const int STR_NUM = 5, STR_LEN = 30;
char origData[STR_NUM][STR_LEN] = {
"Hickory dickory dus. .
.",
"Are you tired of C++?",
"Well, if you have,",
"That's just too bad,",
"There's plenty more for us!"
};
char readData[STR_NUM][STR_LEN] = {{ 0
}};
ofstream
out("Poem.bin", ios::out | ios::binary);
assure(out, "Poem.bin");
for(int i = 0; i < STR_NUM; i++)
out.write(origData[i], STR_LEN);
out.close();
ifstream in("Poem.bin", ios::in |
ios::binary);
assure(in, "Poem.bin");
in.read(readData[0], STR_LEN);
assert(strcmp(readData[0], "Hickory dickory dus.
. .")
== 0);
// Seek -STR_LEN bytes from the end of file
in.seekg(-STR_LEN, ios::end);
in.read(readData[1], STR_LEN);
assert(strcmp(readData[1], "There's plenty more
for us!")
== 0);
// Absolute seek (like using operator[] with a file)
in.seekg(3 * STR_LEN);
in.read(readData[2], STR_LEN);
assert(strcmp(readData[2], "That's just too
bad,") == 0);
// Seek backwards from current position
in.seekg(-STR_LEN * 2, ios::cur);
in.read(readData[3], STR_LEN);
assert(strcmp(readData[3], "Well, if you
have,") == 0);
// Seek from the begining of the file
in.seekg(1 * STR_LEN, ios::beg);
in.read(readData[4], STR_LEN);
assert(strcmp(readData[4], "Are you tired of
C++?")
== 0);
} ///:~
This program writes a poem to a file using a binary output
stream. Since we reopen it as an ifstream, we use seekg( )
to position the get pointer. As you can see, you can seek from the beginning
or end of the file or from the current file position. Obviously, you must
provide a positive number to move from the beginning of the file and a negative
number to move back from the end.
Now that you know about the streambuf and how to
seek, you can understand an alternative method (besides using an fstream
object) for creating a stream object that will both read and write a file. The
following code first creates an ifstream with flags that say it s both
an input and an output file. You can t write to an ifstream, so you need
to create an ostream with the underlying stream buffer:
ifstream in("filename", ios::in | ios::out);
ostream out(in.rdbuf());
You might wonder what happens when you write to one of these
objects. Here s an example:
//: C04:Iofile.cpp
// Reading & writing one file.
#include <fstream>
#include <iostream>
#include "../require.h"
using namespace std;
int main() {
ifstream in("Iofile.cpp");
assure(in, "Iofile.cpp");
ofstream out("Iofile.out");
assure(out, "Iofile.out");
out << in.rdbuf(); // Copy file
in.close();
out.close();
// Open for reading and writing:
ifstream in2("Iofile.out", ios::in |
ios::out);
assure(in2, "Iofile.out");
ostream out2(in2.rdbuf());
cout << in2.rdbuf(); // Print whole file
out2 << "Where does this end up?";
out2.seekp(0, ios::beg);
out2 << "And what about this?";
in2.seekg(0, ios::beg);
cout << in2.rdbuf();
} ///:~
The first five lines copy the source code for this program
into a file called iofile.out and then close the files. This gives us a
safe text file to play with. Then the aforementioned technique is used to
create two objects that read and write to the same file. In cout <<
in2.rdbuf( ), you can see the get pointer is initialized to the
beginning of the file. The put pointer, however, is set to the end of the
file because Where does this end up? appears appended to the file. However,
if the put pointer is moved to the beginning with a seekp( ), all
the inserted text overwrites the existing text. Both writes are seen
when the get pointer is moved back to the beginning with a seekg( ),
and the file is displayed. The file is automatically saved and closed when out2
goes out of scope and its destructor is called.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |