C/C++ File I/O



I/O Redirection

Everyone is familiar with using standard input and standard output via cin and cout. Older C programmers are also familiar with scanf() and prinf().

The easiest type of file I/O is executed from the command line and is known as I/O Redirection because you redirect standard input to read from a file instead of the keyboard and you redirect standard output to write to a file. To use I/O Redirection to read from a file instead of from the keyboard use a command such as myProg < IORedirTest.txt, where myProg is the name of the executable program and IORedirTest.txt is a text file containing lines of text in the same order and format so as to match the order and number of calls to cin. Instead of reading input from the keyboard the program will read its' input from the file.

To use I/O Redirection to write to a file instead of to the screen use a command such as myProg > IOOut.txt, where outputfile.txt is the name of the text file to write to. Instead of writing to the screen output from the program will be written in text format into this file. You can also do both at the same time with a command such as myProg < IORedirTest.txt > IOOut.txt

Standard C File I/O

There are two types of file I/O in ANSI Standard C:

Standard I/O

In Standard I/O there are four types of I/O each with its own functions:
  1. Character
  2. String
  3. Formatted
  4. Record
In order to access any of the ANSI Standard C file I/O functions you must #include <stdio.h>. These can also be used in C++ and all of the functions can be accessed by #include <cstdio> or #include <stdio>. If you use either of the C++ forms of #include then you must also include using namespace std;.

Opening a file in C

The function to open a file is:
FILE *fopen(const char *filename, const char *mode);
The arguments are:
  1. filename – A character array holding the name of the file to open. This can include the path as well.
  2. mode – A character array holding one of the following which tells what you want to do with the file
Most programmers use just "r", "w" or "a" and do the read and write operations in separate functions each of which opens and closes the file. This is safer than leaving a file open while doing internal computations.

Notice that fopen() returns a pointer of a special type called FILE.

Example:

Closing a file in C

To close a file in C call the function fclose(fptr); passing it the pointer to the open file.

Reading and Writing Characters in ANSI Standard C

In Character I/O data is read or written one character at a time. The functions to use are:

Sample Code

#include <stdio.h>

int main(void)
{
    FILE *fptr;  // pointer to FILE
    char ch;

    //-----------------------------------------------
    //  Writing characters to a file
    //  I/O function:  int fputc(int ch, File *fptr)
    //-----------------------------------------------
    // Open the file for writing
    fptr=fopen("textfile.txt","w");

    // Write characters entered from the keyboard till user presses Enter
    while ( (ch = getc(stdin)) != '\n')
        fputc(ch, fptr);

    // Close the file
    fclose(fptr);

    //-----------------------------------------------
    //  Reading characters from a file
    //  I/O function:  int fgetc(File *fptr)
    //-----------------------------------------------
    if( (fptr = fopen("textfile.txt","r")) == NULL)
    {
        printf("Can't open textfile.txt.\n");
        return 0;
    }
    while ( (ch = fgetc(fptr)) != EOF)
        printf("%c", ch);
    fclose(fptr);
    return 0;
}
Note: EOF = -1, this is not a character stored on the disk. It is a flag transmitted by the operating system when it realizes it has read the last character in a file. Since all ASCII characters have an integer value of 0...255 then -1 is unique and can be recognized as EOF. This is also why ch is read by getc() as an int type not a char type.

Reading and Writing Strings in ANSI Standard C

In String I/O data is read or written one line at a time. The functions to use are:

Sample Code

#include <stdio.h>
#include <string.h>

int main(void)
{
    FILE *fptr;        // pointer to FILE

    char string[81];     // Use 80 char lines
    fptr = fopen("textfile.txt","w");
    //-----------------------------------------------
    //  Writing strings to a file
    //  I/O function:  int fputs(char *string, File *fptr)
    //-----------------------------------------------
    while (strlen(gets(string)) > 0)
        {
            fputs(string, fptr); // write string
            fputs("\n", fptr);   // write newline 
        }
    fclose(fptr);

    //-------------------------------------------------------------
    //  Reading strings from a file
    //  I/O function:  char *fgets(char *string, int n, File *fptr)
    //--------------------------------------------------------------
    if((fptr = fopen("textfile.txt","r"))==NULL)
    {
        printf("Can't open textfile.txt.\n");
        return 0;
    }
    while (fgets(string,80,fptr) != NULL)
    {
        string[strlen(string)-1] = '\0';
        printf("%s\n", string);
    }
    fclose(fptr);
    return 0;
}
Note: When writing lines to the file that the '\0' is not written to the file so you must put in a newline character to separate the strings.

Note: When reading lines from the file, you have to explicitly removed the newline character from the string as it is read.

Reading and Writing Formatted I/O in ANSI Standard C

In Formatted I/O data is read or written in a formated form just the same form as scanf() and printf(). The functions to use are:

Sample Code

#include <stdio.h>
#include <string.h>

int main(void)
{
    int inum;
    float fnum;
    char string[81];
    FILE *fptr;
    fptr = fopen("textfile.txt", "w");
    //-----------------------------------------------------------
    //  Writing formatted data to a file
    //  I/O function:  int fprintf(File *fptr, char *format,...)
    //-----------------------------------------------------------
    do
    {
        printf("Type int, float, and string: ");
        scanf("%d %f %s", &inum, &fnum, string);
        fprintf(fptr,"%d %f %s\n",inum,fnum,string);
    }
    while (inum != 0);
    fclose(fptr);

    //-----------------------------------------------------------
    //  Reading formatted data from a file
    //  I/O function:  int fscanf(File *fptr, char *format,...)
    //-----------------------------------------------------------
    if((fptr=fopen("textfile.txt","r"))==NULL)
    {
        printf("Can't open textfile.txt.\n");
        return 0;
    }
    while(fscanf(fptr,"%d %f %s", &inum, &fnum, string) != EOF)
        printf("%d %f %s\n",inum,fnum,string);
    fclose(fptr);
    return 0;
}
Note: This writes the data out in text format so you can read it with any word processor.

Reading and Writing Record (binary) in ANSI Standard C

In Record I/O data is read or written a whole block of memory at a time. The functions to use are:

Sample Code

This example reads simple structures from keyboard and writes to disk. Then reads simple structures from disk and prints to screen.
#include <stdio.h>

struct simple
{
    int num;
    char ch;
};

int main(void)
{
    FILE *fptr;        // pointer to FILE
    simple sim;
    char ch;

    fptr=fopen("structfile.bin", "wb");
    //------------------------------------------------------
    //  Writing structures to a file
    //  I/O function:  long fwrite(void *buffer, long size,
    //                           long numObjects, FILE *fp)
    //------------------------------------------------------
    do
    {
        printf("Type an int and a char.\n");
        scanf("%d %c", &sim.num, &sim.ch);
        fwrite(&sim, sizeof(struct simple), 1, fptr);
        printf("Add another structure to disk(y/n)?  ");
        fflush(stdin); // Flush the keyboard buffer
        ch = getc(stdin);
    }
    while(ch =='y');

    fclose(fptr);
    //------------------------------------------------------
    //  Reading structures from a file
    //  I/O function:  long fread(void *buffer, long size,
    //                           long numObjects, FILE *fp)
    //------------------------------------------------------
    if( (fptr = fopen("structfile.bin", "rb")) == NULL)
    {
        printf("Can't open structfile.bin.\n");
        return 0;
    }
    while (fread(&sim, sizeof(struct simple), 1, fptr)==1)
        printf("%d %c\n", sim.num, sim.ch);
    fclose(fptr);
    return 0;
}
Note: fread() returns an integer value equal to the number of blocks read, ie. if you ask for one structure and fread() returns 0 you know you have reached the end of the file.

Random Access files

With files of structures you can access the stored structures randomly using the fseek() function. fseek() uses a "file pointer", ie. it points to the next position to be read from or written to in the file. Don't confuse this with pointers to type FILE such as *fptr.

Syntax:
	fseek(fptr, offset, mode);
The arguments to fseek are:
  1. fptr - A pointer to an open FILE.
  2. offset - Number of bytes to move the internal file pointer, declared as a long.
  3. mode - The direction in which to move.

Example Calculating offset:
	long offset;
	offset = recno * sizeof(struct simple); // recno = # of record wanted
Remember records are numbered from 0 so if you want to fseek() to the first record then recno=0.

Examples:
	offset = 5 * sizeof(struct simple);
	fseek(fptr, offset, 0); // Find record #6
Here is an example which also checks to be sure that record exists.
	offset = 5 * sizeof(struct simple);
	if (fseek(fptr, offset, 0) != 0)
	{
		printf("Record does not exist.");
		exit();
	}
	fread(&sim, sizeof(struct simple), 1, fptr);

Error Handling

There are a couple of handy error handling functions that you might find useful in file handling.
  1. ferror(fptr); Takes a pointer to a file as its argument and returns 0 (TRUE) if no error occured, or non-zero (FALSE) if some error occurred during the last access of the file.
  2. perror("Error message"); Takes a string that you supply which usually indicates where the error occurred. It prints this string and then it prints a system level error message. For example it can be used after a read or write operation.
    		if ( ferror(fptr) )			// If error 
    		{
    			perror("Disk access error!"); // Report it 
    			fclose(fptr);			// Close file 
    			exit();				// And exit. 
    		}
    

Standard C++ File I/O

C++ introduces the concept of streams. Streams are just a “flow” of characters.

All stream classes are derived from the base class ios_base and its' immediate descendent ios. From these two superclasses are derived. One is istream which becomes the parent class of all input streams and from which standard input cin is derived. The other is ostream which becomes the parent class of all output streams and from which standard output cout is derived.

Similar to C file I/O, C++ file I/O has three basic types:
  1. Character
  2. String
  3. Binary
In order to access any of the ANSI Standard C++ file I/O functions you must #include <iostream.h>. These can also be accessed by #include <iostream>. If you use the name without the .h then you must also include using namespace std;.

Opening a file in C++

Any stream object contains a member function called, appropriately, open. It can take one of three forms:

	fstream 	open(const char *filename, int mode);
	ifstream 	open(const char *filename); 			// Open for input by default
	ofstream 	open(const char *filename); 			// Open for output by default

The arguments are:
  1. filename – A character array holding the name of the file to open. This can include the path as well.
  2. mode – A character array holding one of the following which tells what you want to do with the file. This argument is only needed for fstream objects. An ifstream (input file stream) object is opened for input automatically and an ofstream (output file stream) object is opened for output automatically.
Examples: Any of these will open a file for writing
	Given ofstream outfile then use...

		outfile.open("myfile.txt", ofstream::out);
			or
		outfile.open("myfile.txt", ios::out);
			or
		outfile.open("myfile.txt");

	Given fstream outfile then use...

		outfile.open("myfile.txt", ofstream::out);
			or
		outfile.open("myfile.txt", ios::out);
It is a good idea to check and see if the file was successfully opened, like this:
	if (!outfile.is_open())
	{
		cout << "Can't open file myfile.txt\n";
		exit();
	}
Any of these will open a file for reading
	Given ifstream infile then use...

		infile.open("myfile.txt", ofstream::in); 
			or
		infile.open("myfile.txt", ios::in); 
			or
		infile.open("myfile.txt");	As will these

	Given fstream infile then use...

		infile.open("myfile.txt", ofstream::in);
			or
		infile.open("myfile.txt", ios::in);
As with opening for write check for successful opening for reading:
	if (!infile.is_open())
	{
		cout << "Can't open file myfile.txt\n";
		exit();
	}

Checking State Flags

The use of infile.is_open checks the "open" status of the fstream object. There are a number of other flags you can check.
  1. bad() Returns true if a reading or writing operation fails. This can happen if you try to write to a file that is not open for writing or if the disk you are writing to becomes full.
  2. fail() Returns true in the same cases as bad() but also if a data format error is encountered. This can happen if you read an alphabetic character when you were expecting to read a digit.
  3. eof() Returns true if a file open for reading has reached the end.
  4. good() A generic state flag. Returns false in any of the cases in which the previous flags return true.
Note: If any of these flags gets set you must clear it before attempting any more reads/writes on the file. Do this by calling fstreamobject.clear(). It requires no parameters.

Closing a file

To close a file after reading or writing use the close() member function:
	outfile.close();
	infile.close();

Reading and Writing Characters in ANSI Standard C++

In Character I/O data is read or written one character at a time. The functions to use are:

Sample Code

#include <fstream.h>

int main(void)
{
    ofstream outfile;   // Output file stream
    ifstream infile;    // Input file stream
    char ch;

    outfile.open("textfile.txt", ofstream::out);
    //-----------------------------------------------
    //  Writing characters to a file
    //  I/O function:  outfile.put(char ch)
    //-----------------------------------------------
    cin.get(ch);
    while(ch != '\n')
    {
        outfile.put(ch);
        cin.get(ch);
    }
    outfile.close();

    //-----------------------------------------------
    //  Reading characters from a file
    //  I/O function:  char infile.get()
    //-----------------------------------------------
    infile.open("textfile.txt", ifstream::in);
    cout << "Characters read from file.\n";
    while (infile.good())
    {
        ch = infile.get();
        cout << ch;
    }
    cout << "\n";
    infile.close();
    return 0;
}

Reading and Writing Strings in ANSI Standard C++

In String I/O data is read or written one line at a time. The functions to use are:

Sample Code

#include <fstream.h>

int main(void)
{
    ofstream outfile;    // Output file stream
    ifstream infile;    // Input file stream
    char line[81];     // Use 80 char lines

    outfile.open("textfile.txt", ofstream::out);
    //-----------------------------------------------------------
    //  Writing strings to a file
    //  I/O function:  outfile.write(char *string, int numChars)
    //-----------------------------------------------------------
    cin.getline(line, 80);
    while(strlen(line) > 0)
    {
        outfile.write(line, strlen(line)); // write string
        outfile.write("\n", 1); // write newline
        cin.getline(line, 80);
    }
    outfile.close();

    infile.open("textfile.txt", ifstream::in);
    //-----------------------------------------------------------
    //  Reading strings from a file
    //  I/O function:  infile.getline(char *string, int maxChars)
    //-----------------------------------------------------------
    while (infile.good())
    {
        infile.getline(line, 80);  
        cout << line << "\n"; 
    }
    infile.close();
    return 0;
}

Reading and Writing binary in ANSI Standard C++ using << and >>

The input (<<) and output (>>) operators have been overloaded for binary input and output in C++ file I/O.

Sample Code

#include <fstream.h>

int main(void)
{
    ofstream outfile;    // Output file stream
    ifstream infile;    // Input file stream
    int num;

    outfile.open("binfile.dat", ofstream::binary);
    //-----------------------------------------------
    //  Writing numerical data as binary to a file
    //  I/O function:  Overloaded << operator
    //-----------------------------------------------
    for(int i=0; i<10; i++)
    {
        outfile << i << " \n";
    }
    outfile.close();

    infile.open("binfile.dat ", ifstream::binary);
    //-----------------------------------------------
    //  Reading numerical data as binary from a file
    //  I/O function:  Overloaded >> operator
    //-----------------------------------------------
    while (infile.good())
    {
        infile >> num;
        if(infile.good()) cout << num << " \n";
    }
    infile.close();
    return 0;
}

Reading and Writing binary in ANSI Standard C++ using write and read

Binary I/O in C++ can also be used to write and read records in which data is read or written a whole block of memory at a time. The functions to use are:

Sample Code

#include <fstream.h>

struct simple
{
    int x;
    char ch;
};

int main(void)
{
    ofstream outfile;    // Output file stream
    ifstream infile;    // Input file stream
    simple sim1;

    outfile.open("StructBin.dat", ofstream::binary); 
    if(!outfile.is_open())
    {
       cout << "Unable to open file for output." 
               << "Program terminating.\n";
       return 0;
    }
    //--------------------------------------------------------
    //  Writing structures in binary to a file
    //  I/O function:  outfile.write(const char *, int size)
    //--------------------------------------------------------
    for(int outst=0; outst<10; outst++)
    {
       sim1.x = outst;
       sim1.ch = 'a' + outst;

    // Write 1 structure to the file. Note: the write() function 
    // expects a const char* as the first argument.  Use the 
    // reinterpret_cast operator to cast the structure pointer to a 
    // const char*.  
       outfile.write(reinterpret_cast<const char *>(&sim1), 
                    sizeof(simple));  
    }
    outfile.close();

    infile.open("StructBin.dat", ifstream::binary);
    if(!infile.is_open())
    {
        cout << "Unable to open file for input." <<
                     "Program terminating.\n";
        return 0;
    }
    //--------------------------------------------------------
    //  Reading structures in binary from a file
    //  I/O function:  infile.read(char *, int size)
    //--------------------------------------------------------
    while(!infile.eof()) 
    {
    // Read 1 structure from the file. Note: the read() function 
    // expects a char* as the first argument.  We use the 
    // reinterpret_cast operator to cast the structure pointer to a 
    // char*.  
        infile.read(reinterpret_cast<char *>(&sim1), 
            sizeof(simple));
        if(!infile.eof())
            cout << sim1.x << " -- " << sim1.ch << "\n";  
    }
    infile.close();
    return 0;
}

Random Access Files in C++

All stream objects have at least one internal stream "pointer" which marks the next position in the file where reading or writing will occur. Input file objects (ifstream and istream) have a get pointer that points to the next point in the file to be read from. Output file objects (ofstream and ostream) have a put pointer that points to the next point in the file to be written to. Usually this refers to the end of the file. An fstream object can have both. The functions to call to get the locations in a file are: The functions to call to change the locations in a file are: You can also move the pointers from their current location to an offset in bytes by specifying the number of bytes to move and the position to move from. ...where direction is one of the enum values: Example code calculating the size in bytes of a file
// obtaining file size
#include <iostream>
#include <fstream>
using namespace std;

int main () 
{
	long begin,end;
	ifstream myfile;
	myfile.open("example.txt");
	begin = myfile.tellg();
	myfile.seekg (0, ios::end);
	end = myfile.tellg();
	myfile.close();
	cout << "size is: " << (end-begin) << " bytes.\n";
	return 0;
}

Passing streams to functions

Frequently you need to pass a file stream into a function. Care must be taken in doing this. File streams must be passed to functions by reference, not by value, i.e. the function prototypes must look like this…

           void myFileFunction(ifstream &fp, …)

not like this…

           void myFileFunction(ifstream fp, …)

Warnings: