Search

13.7 — Random file I/O

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 it’s 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 the seekg() function (for input) and seekp() function (for output). In case you are wondering, the g stands for “get” and the p for “put”.

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:

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

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

Heres the example:

This produces the result:

is line 1
line 2
his is line 4

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:

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 a seek operation. If you don’t actually want to move the file pointer, you can always seek to the current position:

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

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

One other bit of trickiness: Unlike istream, 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 it’s contents, and changes the any vowels it finds to a ‘#’ symbol.

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.

14.1 -- Function templates
Index
13.6 -- Basic file I/O

12 comments to 13.7 — Random file I/O

  • loift

    I need to do many manipulations on a file on a bit level. Is there a better way to do this than: (1)open the file in input mode (2)open a second file in output mode (3)read the input file as a string (4)individually convert each character of this string to its binary (ascii/utf-8) value and append/write this to the output file (5)do manipulations on the output file (6) manually convert the output file back by reading the output file as a string 8 "boolean" characters at a time and turning it into an ascii/utf-8 value.

  • hi..I want to find whether a string is present or not in a file which i have already written and its contents are being displayed...can someone help me with the code...

  • Casey

    Is it possible to pass fstream objects as parameters? I am writing a simple function that calculates the size of a file stream passed to it. Here is the code.

    //Obtain the size of the file without moving the file pointer
    unsigned long fileSize(fstream file)
    {
    	unsigned long position = file.tellg();
    
    	//Move pointer to end
    	file.seekg(0, ios::end);
    
    	//Record size in bytes
    	unsigned long size = file.tellg();
    
    	//Return file pointer to original location
    	file.seekg(position, ios::beg);
    
    	return size;
    }
    

    Any help is appreciated.

    Also, is there a tutorial on advanced operations with binary files? I'm writing a file archive application and I need a little help with the C++ filestream objects.
    I would like to weave files together byte by byte. I would also like to write variables, like the number and names of files in the archive, to the top of the file for easy reference.

    Thank you for these tutorials. They have been a great resource for me.

    • Casey

      I figured out my problem. The ios namespace lives inside std, so to use ios, I must also use std. My fstream parameters were being declared outside of their scope.

      Man, this stuff gets confusing!

  • Hello
    why this code is true?

    
    #include<iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    	int b[4]={1,1,5,1};
    	int c[4]={0};
    	fstream A("File.txt",ios::binary|ios::in|ios::out);
    	if(!A)
    	{
    		return 1;
    	}
    	A.write((char *) (&b),sizeof(b));//(&b) must be (b) but ?????!!!!!
    	A.seekg(0);
    	A.read((char *) (c),sizeof(c));
    	cout<<c[2];
    	return 0;
    }
    
    
  • Hello
    why this code is true?

    
    #include
    #include 
    using namespace std;
    
    int main()
    {
    	int b[4]={1,1,5,1};
    	int c[4]={0};
    	fstream A("File.txt",ios::binary|ios::in|ios::out);
    
    	if(!A)
    	{
    		return 1;
    	}
    	A.write((char *) (&b),sizeof(b));//(&b) must be (b) but ?????!!!!!
    	A.seekg(0);
    	A.read((char *) (c),sizeof(c));
    	cout<<c[2];
    	return 0;
    }
  • dekaya

    I modified your code slightly to get it to work on my win2k machine.
    using dev-cpp

    I added :

     
    #include <fstream>// ***  added for fstreams
    
    
    using namespace std; //moved outside of main
    
    //and finally opened in binary mode
    fstream iofile("Sample.dat", ios::binary | ios::in | ios::out);
    // **** the binary part is important otherwise the position is off
    
    
  • Sam

    Hi, I tried your vowel replacement program. It works for single-line text files, but if there's more than 1 line, the program seems to break down. Even the first line isn't "translated" properly.

    Input:
    This is line One.
    This is line Two.

    Output:
    This#is#lin# On#.
    Thi# i# li#e#Two#

    This only thing I changed was the .dat extension to .txt

    Seems like the 'new line' character screws up the either the get or put pointer, although this doesn't explain why even the first line isn't working properly.

    • Hi Sam/Alex,

      My results were not quite the same as Sam's but the code did not work.

      The problem I believe lies in the seekg(0, ios::cur) call after we output the '#'. As far as I can make out this is not good enough to convince the io system that something has happened (internally it optimises away the call to fseek and I think that means its idea of where we are is not correct).

      Change that line for these two:

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

      That worked for me.

      What I can't understand is why we don't need to use seekp (instead of seekg) before the write...

      Grant

      • I think you can also use:

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

        Grant

        • Hmmm, that is interesting about seekg(0, ios::cur) not working for you. It worked fine for me, but maybe it is being optimized away in your case as you suggest.

          I added a short blurb about tellg() and tellp() to the tutorial and also changed the example to use iofile.seekg(iofile.tellg(), ios::beg);, though I do have some concerns about the performance ramificaitons of doing such a thing (not sure if it's smart enough to convert that into a relative position, or whether it's going back to the beginning of the file each time and then counting it out).

          As for the seekg()/seekp() difference, as far as I can tell with fstream they appear to be identical.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">