Search

Enter your search terms

Submit search form

18.7 — Random file I/O

By Alex on April 4th, 2008 | last modified by Alex on January 4th, 2018

The file pointer

Each file stream class contains a file pointer that is used to keep track of the current read/write position within the file. When something is read from or written to a file, the reading/writing happens at the file pointer’s current location. By default, when opening a file for reading or writing, the file pointer is set to the beginning of the file. However, if a file is opened in append mode, the file pointer is moved to the end of the file, so that writing does not overwrite any of the current contents of the file.

Random file access with seekg() and seekp()

So far, all of the file access we’ve done has been sequential -- that is, we’ve read or written the file contents in order. However, it is also possible to do random file access -- that is, skip around to various points in the file to read its contents. This can be useful when your file is full of records, and you wish to retrieve a specific record. Rather than reading all of the records until you get to the one you want, you can skip directly to the record you wish to retrieve.

Random file access is done by manipulating the file pointer using either seekg() function (for input) and seekp() function (for output). In case you are wondering, the g stands for “get” and the p for “put”. For some types of streams, seekg() (changing the read position) and seekp() (changing the write position) operate independently -- however, with file streams, the read and write position are always identical, so seekg and seekp can be used interchangeably.

The seekg() and seekp() functions take two parameters. The first parameter is an offset that determines how many bytes to move the file pointer. The second parameter is an Ios flag that specifies what the offset parameter should be offset from.

Ios seek flag

Meaning

beg

The offset is relative to the beginning of the file (default)

cur

The offset is relative to the current location of the file pointer

end

The offset is relative to the end of the file

A positive offset means move the file pointer towards the end of the file, whereas a negative offset means move the file pointer towards the beginning of the file.

Here are some examples:

1

2

3

4

5

inf.seekg(14,ios::cur);// move forward 14 bytes

inf.seekg(-18,ios::cur);// move backwards 18 bytes

inf.seekg(22,ios::beg);// move to 22nd byte in file

inf.seekg(24);// move to 24th byte in file

inf.seekg(-28,ios::end);// move to the 28th byte before end of the file

Moving to the beginning or end of the file is easy:

1

2

inf.seekg(0,ios::beg);// move to beginning of file

inf.seekg(0,ios::end);// move to end of file

Let’s do an example using seekg() and the input file we created in the last lesson. That input file looks like this:

This is line 1
This is line 2
This is line 3
This is line 4

Here is the example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

intmain()

{

usingnamespacestd;

ifstreaminf("Sample.dat");

// If we couldn't open the input file stream for reading

if(!inf)

{

// Print an error and exit

cerr<<"Uh oh, Sample.dat could not be opened for reading!"<<endl;

exit(1);

}

stringstrData;

inf.seekg(5);// move to 5th character

// Get the rest of the line and print it

getline(inf,strData);

cout<<strData<<endl;

inf.seekg(8,ios::cur);// move 8 more bytes into file

// Get rest of the line and print it

getline(inf,strData);

cout<<strData<<endl;

inf.seekg(-15,ios::end);// move 15 bytes before end of file

// Get rest of the line and print it

getline(inf,strData);

cout<<strData<<endl;

return0;

}

This produces the result:

is line 1
line 2
his is line 4

Note: Some compilers have buggy implementations of seekg() and tellg() when used in conjunction with text files (due to buffering). If your compiler is one of them (and you’ll know because your output will differ from the above), you can try opening the file in binary mode instead:

1

ifstreaminf("Sample.dat",ifstream::binary);

Two other useful functions are tellg() and tellp(), which return the absolute position of the file pointer. This can be used to determine the size of a file:

1

2

3

ifstreaminf("Sample.dat");

inf.seekg(0,ios::end);// move to end of file

cout<<inf.tellg();

This prints:

64

which is how long sample.dat is in bytes (assuming a carriage return after the last line).

Reading and writing a file at the same time using fstream

The fstream class is capable of both reading and writing a file at the same time -- almost! The big caveat here is that it is not possible to switch between reading and writing arbitrarily. Once a read or write has taken place, the only way to switch between the two is to perform an operation that modifies the file position (e.g. a seek). If you don’t actually want to move the file pointer, you can always seek to the current position:

1

2

// assume iofile is an object of type fstream

iofile.seekg(iofile.tellg(),ios::beg);// seek to current file position

If you do not do this, any number of strange and bizarre things may occur.

(Note: Although it may seem that iofile.seekg(0, ios::cur) would also work, it appears some compilers may optimize this away.)

One other bit of trickiness: Unlike ifstream, where we could say while (inf) to determine if there was more to read, this will not work with fstream.

Let’s do a file I/O example using fstream. We’re going to write a program that opens a file, reads its contents, and changes any vowels it finds to a ‘#’ symbol.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

intmain()

{

usingnamespacestd;

// Note we have to specify both in and out because we're using fstream

fstream iofile("Sample.dat",ios::in|ios::out);

// If we couldn't open iofile, print an error

if(!iofile)

{

// Print an error and exit

cerr<<"Uh oh, Sample.dat could not be opened!"<<endl;

exit(1);

}

charchChar;// we're going to do this character by character

// While there's still data to process

while(iofile.get(chChar))

{

switch(chChar)

{

// If we find a vowel

case'a':

case'e':

case'i':

case'o':

case'u':

case'A':

case'E':

case'I':

case'O':

case'U':

// Back up one character

iofile.seekg(-1,ios::cur);

// Because we did a seek, we can now safely do a write, so

// let's write a # over the vowel

iofile<<'#';

// Now we want to go back to read mode so the next call

// to get() will perform correctly. We'll seekg() to the current

// location because we don't want to move the file pointer.

iofile.seekg(iofile.tellg(),ios::beg);

break;

}

}

return0;

}

Other useful file functions

To delete a file, simply use the remove() function.

Also, the is_open() function will return true if the stream is currently open, and false otherwise.

A warning about writing pointers to disk

While streaming variables to a file is quite easy, things become more complicated when you’re dealing with pointers. Remember that a pointer simply holds the address of the variable it is pointing to. Although it is possible to read and write addresses to disk, it is extremely dangerous to do so. This is because a variable’s address may differ from execution to execution. Consequently, although a variable may have lived at address 0x0012FF7C when you wrote that address to disk, it may not live there any more when you read that address back in!

For example, let’s say you had an integer named nValue that lived at address 0x0012FF7C. You assigned nValue the value 5. You also declared a pointer named *pnValue that points to nValue. pnValue holds nValue’s address of 0x0012FF7C. You want to save these for later, so you write the value 5 and the address 0x0012FF7C to disk.

A few weeks later, you run the program again and read these values back from disk. You read the value 5 into another variable named nValue, which lives at 0x0012FF78. You read the address 0x0012FF7C into a new pointer named *pnValue. Because pnValue now points to 0x0012FF7C when the nValue lives at 0x0012FF78, pnValue is no longer pointing to nValue, and trying to access pnValue will lead you into trouble.

Rule: Do not write addresses to files. The variables that were originally at those addresses may be at different addresses when you read their values back in from disk, and the addresses will be invalid.

66 comments to 18.7 — Random file I/O

For file streams, there's only one file pointer, so seekg() and seekp() operate identically. It's probably slightly more correct to use seekp() when intending to write, but functionally it doesn't make a difference in this case.

Thanks for the awesome tutorial alex, you did save me when I was looking for a place to learn c++. If you let me ask you a last question, what book would you think would fit more for me right now between "The C++ Standard Library: A Tutorial and Reference" (which you did indicate) and "Effective Modern C++", I'll start learning SFML.

They're totally different books. The C++ Standard Library book is meant to teach you about the standard library in a lot more detail. Effective Modern C++ is meant to teach you how to use some of C++'s more advanced features. Personally, I think you'll get more mileage out of the C++ Standard Library book, as it's always better to reuse what already exists than build new things yourself if you can.

Hi Alex,
I am having problem saving dynamically allocated array into files. While adding static arrays to file works fine, when storing dynamic arrays the program starts to crash..
Is there a different way to store dynamic allocated data to files? I have a class with dynamic array and i am storing data to file with following commands:

Assuming that t1 is a pointer, sizeof(t1) would be the size of the pointer. What you actually want is the size of the whole array. You can do this by multiplying the array length by the size of the first element (*t1).

Hello Alex, firstly thank you for these tutorials i recently completed it.
can you please suggest me a project than can be done using the knowledge i have attained here, and would also add value to my resume